Testing a switch-statement using Jest & Enzyme - unit-testing

I'm using a switch-statement to conditionally render components and I've been spending quite some time attempting to test but I just have no idea how to go about it and havent found any helpful resources online for this. Thanks for taking a look!
componentToRender = (currentPath) => {
const { years, tours, songs, shows, venues } = this.props;
switch (currentPath) {
case "/Years":
return years.map(year => <Years key={year.date} year={year} />);
case "/Tours":
return tours.map(tour => <Tours key={tour.id} tour={tour} />);
case "/Songs":
return songs.map(song => <Songs key={song.id} song={song} />);
case "/Venues":
return venues.map(venue => <Venues key={venue.id} venue={venue} />);
case "/Shows":
return shows.map(show => <Shows key={show.id} show={show} />);
case "/SetList":
return <SetLists />;
case "/UserStats":
return <UserStats />;
default:
return <HomePage />;
}
};

Here is a solution:
index.tsx:
import React, { Component } from 'react';
const Years = ({ key, year }) => (
<div>
{key}, {year}
</div>
);
const Tours = ({ key, tour }) => (
<div>
{key}, {tour}
</div>
);
const Songs = ({ key, song }) => (
<div>
{key}, {song}
</div>
);
const Venues = ({ key, venue }) => (
<div>
{key}, {venue}
</div>
);
const Shows = ({ key, show }) => (
<div>
{key}, {show}
</div>
);
const SetLists = () => <div>SetLists</div>;
const UserStats = () => <div>UserStats</div>;
const HomePage = () => <div>HomePage</div>;
export interface IXComponentProps {
years: any[];
tours: any[];
songs: any[];
shows: any[];
venues: any[];
currentPath: string;
}
export class XComponent extends Component<IXComponentProps> {
constructor(props) {
super(props);
}
public componentToRender = currentPath => {
const { years, tours, songs, shows, venues } = this.props;
switch (currentPath) {
case '/Years':
return years.map(year => <Years key={year.date} year={year} />);
case '/Tours':
return tours.map(tour => <Tours key={tour.id} tour={tour} />);
case '/Songs':
return songs.map(song => <Songs key={song.id} song={song} />);
case '/Venues':
return venues.map(venue => <Venues key={venue.id} venue={venue} />);
case '/Shows':
return shows.map(show => <Shows key={show.id} show={show} />);
case '/SetList':
return <SetLists />;
case '/UserStats':
return <UserStats />;
default:
return <HomePage />;
}
}
public render() {
const { currentPath } = this.props;
return this.componentToRender(currentPath);
}
}
index.spec.tsx:
import React from 'react';
import { shallow, ShallowWrapper } from 'enzyme';
import { XComponent, IXComponentProps } from './';
describe('XComponent', () => {
let wrapper: ShallowWrapper;
const mockedProps: IXComponentProps = {
years: [{ date: '2019-01-01' }],
tours: [{ id: '1' }],
songs: [{ id: '2' }],
shows: [{ id: '3' }],
venues: [{ id: '4' }],
currentPath: ''
};
beforeEach(() => {
wrapper = shallow(<XComponent {...mockedProps}></XComponent>);
});
it.each`
currentPath | componentToRender
${'/'} | ${'HomePage'}
${'/UserStats'} | ${'UserStats'}
${'/SetList'} | ${'SetLists'}
${'/Shows'} | ${'Shows'}
${'/Venues'} | ${'Venues'}
${'/Songs'} | ${'Songs'}
${'/Tours'} | ${'Tours'}
${'/Years'} | ${'Years'}
`(
'should render $componentToRender component by current path $currentPath correctly',
({ currentPath, componentToRender }) => {
wrapper.setProps({ currentPath });
expect(wrapper.find(componentToRender)).toHaveLength(1);
}
);
});
Unit test result with coverage report:
PASS src/stackoverflow/56453372/index.spec.tsx (7.661s)
XComponent
✓ should render HomePage component by current path / correctly (19ms)
✓ should render UserStats component by current path /UserStats correctly (1ms)
✓ should render SetLists component by current path /SetList correctly (2ms)
✓ should render Shows component by current path /Shows correctly (1ms)
✓ should render Venues component by current path /Venues correctly (1ms)
✓ should render Songs component by current path /Songs correctly (2ms)
✓ should render Tours component by current path /Tours correctly (1ms)
✓ should render Years component by current path /Years correctly (1ms)
-----------|----------|----------|----------|----------|-------------------|
File | % Stmts | % Branch | % Funcs | % Lines | Uncovered Line #s |
-----------|----------|----------|----------|----------|-------------------|
All files | 67.86 | 100 | 52.94 | 100 | |
index.tsx | 67.86 | 100 | 52.94 | 100 | |
-----------|----------|----------|----------|----------|-------------------|
Test Suites: 1 passed, 1 total
Tests: 8 passed, 8 total
Snapshots: 0 total
Time: 9.53s
HTML coverage report:
Here is the completed demo: https://github.com/mrdulin/jest-codelab/tree/master/src/stackoverflow/56453372

Related

How to write unit test case for JWT strategy

I am new to passport.js and trying to cover the unit test case for my JWT strategy. Can anyone suggest how to do that?
// Setup JWT strategy for all requests
passport.use(
new JWTStrategy(
{
jwtFromRequest: ExtractJwt.fromAuthHeaderAsBearerToken(),
secretOrKey: JWT_PRIVATE_KEY,
},
async (jwtPayload: any, done: any) => {
const isUser = jwtPayload.type === EntityType.User;
const model = isUser ? userModel : vendorModel;
try {
const document = await model.findOne({ _id: jwtPayload.id });
if (document) {
return done(null, jwtPayload);
} else {
return done(null, false);
}
} catch (err) {
return done(err, false);
}
},
),
);
Unit test solution:
index.ts:
import passport from 'passport';
import { Strategy as JWTStrategy, ExtractJwt } from 'passport-jwt';
import { userModel, vendorModel, EntityType } from './models';
const JWT_PRIVATE_KEY = 'secret 123';
passport.use(
new JWTStrategy(
{
jwtFromRequest: ExtractJwt.fromAuthHeaderAsBearerToken(),
secretOrKey: JWT_PRIVATE_KEY,
},
async (jwtPayload: any, done: any) => {
console.log('123123');
const isUser = jwtPayload.type === EntityType.User;
const model = isUser ? userModel : vendorModel;
try {
const document = await model.findOne({ _id: jwtPayload.id });
if (document) {
return done(null, jwtPayload);
} else {
return done(null, false);
}
} catch (err) {
return done(err, false);
}
},
),
);
models.ts:
export enum EntityType {
User = 'User',
}
export const userModel = {
async findOne(opts) {
return 'real user document';
},
};
export const vendorModel = {
async findOne(opts) {
return 'real vendor document';
},
};
index.test.ts:
import { Strategy as JWTStrategy, ExtractJwt, VerifyCallback, StrategyOptions } from 'passport-jwt';
import passport from 'passport';
import { userModel, vendorModel } from './models';
jest.mock('passport-jwt', () => {
const mJWTStrategy = jest.fn();
const mExtractJwt = {
fromAuthHeaderAsBearerToken: jest.fn(),
};
return { Strategy: mJWTStrategy, ExtractJwt: mExtractJwt };
});
jest.mock('passport', () => {
return { use: jest.fn() };
});
describe('62125872', () => {
let verifyRef;
beforeEach(() => {
const mJwtFromRequestFunction = jest.fn();
(ExtractJwt.fromAuthHeaderAsBearerToken as jest.MockedFunction<
typeof ExtractJwt.fromAuthHeaderAsBearerToken
>).mockReturnValueOnce(mJwtFromRequestFunction);
(JWTStrategy as jest.MockedClass<any>).mockImplementation((opt: StrategyOptions, verify: VerifyCallback) => {
verifyRef = verify;
});
});
it('should verify using user model and call done with jwtpayload if user document exists', async () => {
const payload = { type: 'User', id: 1 };
const mDone = jest.fn();
jest.spyOn(userModel, 'findOne').mockResolvedValueOnce('mocked user document');
await import('./');
await verifyRef(payload, mDone);
expect(passport.use).toBeCalledWith(expect.any(Object));
expect(JWTStrategy).toBeCalledWith(
{ jwtFromRequest: expect.any(Function), secretOrKey: 'secret 123' },
expect.any(Function),
);
expect(ExtractJwt.fromAuthHeaderAsBearerToken).toBeCalledTimes(1);
expect(userModel.findOne).toBeCalledWith({ _id: 1 });
expect(mDone).toBeCalledWith(null, { type: 'User', id: 1 });
});
it("should verify using user model and call done with false if user document doesn't exist", async () => {
const payload = { type: 'User', id: 1 };
const mDone = jest.fn();
jest.spyOn(userModel, 'findOne').mockResolvedValueOnce('');
await import('./');
await verifyRef(payload, mDone);
expect(passport.use).toBeCalledWith(expect.any(Object));
expect(JWTStrategy).toBeCalledWith(
{ jwtFromRequest: expect.any(Function), secretOrKey: 'secret 123' },
expect.any(Function),
);
expect(ExtractJwt.fromAuthHeaderAsBearerToken).toBeCalledTimes(1);
expect(userModel.findOne).toBeCalledWith({ _id: 1 });
expect(mDone).toBeCalledWith(null, false);
});
// you can do the rest parts
});
Unit test results:
PASS stackoverflow/62125872/index.test.ts
62125872
✓ should verify using user model and call done with jwtpayload if user document exists (11ms)
✓ should verify using user model and call done with false if user document doesn't exist (2ms)
-----------|---------|----------|---------|---------|-------------------
File | % Stmts | % Branch | % Funcs | % Lines | Uncovered Line #s
-----------|---------|----------|---------|---------|-------------------
All files | 85 | 83.33 | 60 | 84.21 |
index.ts | 92.86 | 75 | 100 | 92.31 | 24
models.ts | 66.67 | 100 | 33.33 | 66.67 | 6,11
-----------|---------|----------|---------|---------|-------------------
Test Suites: 1 passed, 1 total
Tests: 2 passed, 2 total
Snapshots: 0 total
Time: 3.716s, estimated 10s
Using supertest to verify full cycle
import request from 'supertest';
import express from 'express';
import jwt from 'jsonwebtoken'
export const createAuthToken = (userId) => {
const body = {
type: EntityType.User,
id: userId,
};
return jwt.sign(body, JWT_PRIVATE_KEY);
};
// this function should configure express app
const appLoader = async app => {
(await import('../app/loaders/express')).expressLoader({ app }); // express bindings and routes
await import('./'); // passport config
}
describe('passport-jwt auth', () => {
const app = express();
const token = createAuthToken('user1')
beforeAll(async () => {
await appLoader({ app });
});
it('should verify auth', async () => {
jest.spyOn(userModel, 'findOne').mockResolvedValueOnce('mocked user document');
await request(app)
.get('/protected-endpoint')
.set('Authorization', `Bearer ${token}`)
.expect(200);
});
it('should verify auth - failure', async () => {
await request(app)
.get('/protected-endpoint')
.set('Authorization', `Bearer wrong-token`)
.expect(401);
});
});

Mocking ApolloClient's client.query method with Jest

Update January 22nd 2020
The solution from #slideshowp2 is correct, but I could not get it to work at all, due to this TypeError:
TypeError: Cannot read property 'query' of undefined
Well it turned out to be my jest configuration that had resetMocks: true set. After I removed it, the test did pass. (I don't know why though)
Original question:
I need to execute a graphql query in a helper function outside of a React component using Apollo Client and after a bit of trial and error I went for this approach which is working as it is supposed to:
setup.ts
export const setupApi = (): ApolloClient<any> => {
setupServiceApi(API_CONFIG)
return createServiceApolloClient({ uri: `${API_HOST}${API_PATH}` })
}
getAssetIdFromService.ts
import { setupApi } from '../api/setup'
const client = setupApi()
export const GET_ASSET_ID = gql`
query getAssetByExternalId($externalId: String!) {
assetId: getAssetId(externalId: $externalId) {
id
}
}
`
export const getAssetIdFromService = async (externalId: string) => {
return await client.query({
query: GET_ASSET_ID,
variables: { externalId },
})
return { data, errors, loading }
}
Now I am trying to write test tests for the getAssetIdFromService function, but I have trouble figuring out how to get the client.query method to work in tests.
I have tried the approach below including many others that did not work.
For this particular setup, jest throws
TypeError: client.query is not a function
import { setupApi } from '../../api/setup'
import { getAssetIdFromService } from '../getAssetIdFromService'
jest.mock('../../api/setup', () => ({
setupApi: () => jest.fn(),
}))
describe('getAssetIdFromService', () => {
it('returns an assetId when passed an externalId and the asset exists in the service', async () => {
const { data, errors, loading } = await getAssetIdFromService('e1')
// Do assertions
})
}
I assume I am missing something in relation to this part:
jest.mock('../../api/setup', () => ({
setupApi: () => jest.fn(),
}))
...but I cannot see it.
You didn't mock correctly. Here is the correct way:
getAssetIdFromService.ts:
import { setupApi } from './setup';
import { gql } from 'apollo-server';
const client = setupApi();
export const GET_ASSET_ID = gql`
query getAssetByExternalId($externalId: String!) {
assetId: getAssetId(externalId: $externalId) {
id
}
}
`;
export const getAssetIdFromService = async (externalId: string) => {
return await client.query({
query: GET_ASSET_ID,
variables: { externalId },
});
};
setup.ts:
export const setupApi = (): any => {};
getAssetIdFromService.test.ts:
import { getAssetIdFromService, GET_ASSET_ID } from './getAssetIdFromService';
import { setupApi } from './setup';
jest.mock('./setup.ts', () => {
const mApolloClient = { query: jest.fn() };
return { setupApi: jest.fn(() => mApolloClient) };
});
describe('59829676', () => {
it('should query and return data', async () => {
const client = setupApi();
const mGraphQLResponse = { data: {}, loading: false, errors: [] };
client.query.mockResolvedValueOnce(mGraphQLResponse);
const { data, loading, errors } = await getAssetIdFromService('e1');
expect(client.query).toBeCalledWith({ query: GET_ASSET_ID, variables: { externalId: 'e1' } });
expect(data).toEqual({});
expect(loading).toBeFalsy();
expect(errors).toEqual([]);
});
});
Unit test results with 100% coverage:
PASS apollo-graphql-tutorial src/stackoverflow/59829676/getAssetIdFromService.test.ts (8.161s)
59829676
✓ should query and return data (7ms)
--------------------------|----------|----------|----------|----------|-------------------|
File | % Stmts | % Branch | % Funcs | % Lines | Uncovered Line #s |
--------------------------|----------|----------|----------|----------|-------------------|
All files | 100 | 100 | 100 | 100 | |
getAssetIdFromService.ts | 100 | 100 | 100 | 100 | |
--------------------------|----------|----------|----------|----------|-------------------|
Test Suites: 1 passed, 1 total
Tests: 1 passed, 1 total
Snapshots: 0 total
Time: 8.479s
Source code: https://github.com/mrdulin/apollo-graphql-tutorial/tree/master/src/stackoverflow/59829676
Use blow mock class:
class ApolloClient {
constructor(uri: string, fetch: any, request: any) {}
setupApi() {
return {
query: jest.fn(),
};
}
query() {
return jest.fn();
}
}
module.exports = ApolloClient;
and add below line to jest.cofig.ts
moduleNameMapper: {
'apollo-boost': '<rootDir>/.jest/appolo-client.ts',
},

Jest mocking document.referrer

I have the following method in a react app service which requires Unit Testing.
onDeclineCallback = () => {
console.log("boom " + document.referrer);
if (document.referrer === "") {
console.log("redirect to our age policy page");
} else {
history.back();
}
};
My unit test currently looks like:
history.back = jest.fn(); // Mocking history.back as jest fn
describe('age verifiction service test', () => {
it('returns user to referrer if declined and referrer is available', () => {
document = {
...document,
referrer: 'Refferer Test', // My hacky attempt at mocking the entire document object
};
ageVerification.onDeclineCallback();
expect(history.back).toHaveBeenCalledTimes(1);
});
});
I am trying to find a way of mocking document.referrer in order to write a unit test for each case. Can anyone provide an approach for this?
You can use Object.defineProperty method to set a mocked value of document.referrer.
E.g.
index.ts:
export class AgeVerification {
public onDeclineCallback = () => {
console.log('boom ' + document.referrer);
if (document.referrer === '') {
console.log('redirect to our age policy page');
} else {
history.back();
}
};
}
index.spec.ts:
import { AgeVerification } from './';
describe('age verifiction service test', () => {
let ageVerification;
beforeEach(() => {
ageVerification = new AgeVerification();
history.back = jest.fn();
});
afterAll(() => {
jest.restoreAllMocks();
jest.resetAllMocks();
});
it('returns user to referrer if declined and referrer is available', () => {
const originalReferrer = document.referrer;
Object.defineProperty(document, 'referrer', { value: 'Refferer Test', configurable: true });
ageVerification.onDeclineCallback();
expect(history.back).toHaveBeenCalledTimes(1);
Object.defineProperty(document, 'referrer', { value: originalReferrer });
});
it('should print log', () => {
const logSpy = jest.spyOn(console, 'log');
ageVerification.onDeclineCallback();
expect(logSpy.mock.calls[0]).toEqual(['boom ']);
expect(logSpy.mock.calls[1]).toEqual(['redirect to our age policy page']);
});
});
Unit test result with 100% coverage:
PASS src/stackoverflow/59198002/index.test.ts (13.915s)
age verifiction service test
✓ returns user to referrer if declined and referrer is available (17ms)
✓ should print log (3ms)
console.log src/stackoverflow/59198002/index.ts:264
boom Refferer Test
console.log node_modules/jest-mock/build/index.js:860
boom
console.log node_modules/jest-mock/build/index.js:860
redirect to our age policy page
----------|----------|----------|----------|----------|-------------------|
File | % Stmts | % Branch | % Funcs | % Lines | Uncovered Line #s |
----------|----------|----------|----------|----------|-------------------|
All files | 100 | 100 | 100 | 100 | |
index.ts | 100 | 100 | 100 | 100 | |
----------|----------|----------|----------|----------|-------------------|
Test Suites: 1 passed, 1 total
Tests: 2 passed, 2 total
Snapshots: 0 total
Time: 15.385s
Source code: https://github.com/mrdulin/jest-codelab/tree/master/src/stackoverflow/59198002
worked for me, the jest mock way:
jest.spyOn(document, 'referrer', 'get').mockReturnValue('mock ref value');

Vue.js + Avoriaz : how to test a watcher?

I am trying to test the following component w Avoriaz, but upon props change , the action in watch: {} is not triggered
ItemComponent.vue
switch checkbox
✗ calls store action updateList when item checkbox is switched
AssertionError: expected false to equal true
at Context.<anonymous> (webpack:///test/unit/specs/components/ItemComponent.spec.js:35:47 <- index.js:25510:48)
thanks for feedback
ItemComponent.vue
<template>
<li :class="{ 'removed': item.checked }">
<div class="checkbox">
<label>
<input type="checkbox" v-model="item.checked"> {{ item.text }}
</label>
</div>
</li>
</template>
<script>
import { mapActions } from 'vuex'
export default {
props: ['item', 'id'],
methods: mapActions(['updateList']),
watch: {
'item.checked': function () {
this.updateList(this.id)
}
}
}
</script>
here is my component test
ItemComponent.spec.js
import Vue from 'vue'
import ItemComponent from '#/components/ItemComponent'
import Vuex from 'vuex'
import sinon from 'sinon'
import { mount } from 'avoriaz'
Vue.use(Vuex)
describe('ItemComponent.vue', () => {
let actions
let store
beforeEach(() => {
actions = {
updateList: sinon.stub()
}
store = new Vuex.Store({
state: {},
actions
})
})
describe('switch checkbox', () => {
it('calls store action updateList when item checkbox is switched', () => {
const id = '3'
const item = { text: 'Bananas', checked: true }
const wrapper = mount(ItemComponent, { propsData: { item, id }, store })
// switch item checked to false
wrapper.setProps({ item: { text: 'Bananas', checked: false } })
expect(wrapper.vm.$props.item.checked).to.equal(false)
expect(actions.updateList.calledOnce).to.equal(true)
})
})
})
U mistaked the prop,use :checked instead
I should write my expect(actions.updateList() . within a $nextTick block
describe('switch checkbox', () => {
it('calls store action updateList when item checkbox is switched', (done) => {
const id = '3'
const item = { text: 'Bananas', checked: true }
const wrapper = mount(ItemComponent, { propsData: { item, id }, store })
// switch item.checked to false
wrapper.setProps({ item: { text: 'Bananas', checked: false } })
expect(wrapper.vm.$props.item.checked).to.equal(false)
wrapper.find('input')[0].trigger('input')
wrapper.vm.$nextTick(() => {
expect(actions.updateList.calledOnce).to.equal(true)
done()
})
})
})
then my test is OK
ItemComponent.vue
switch checkbox
✓ calls store action updateList when item checkbox is switched

Test Action of Redux-thunk use JEST

I want to write a test for Axios use Jest Framework. I'm using Redux.
Here is my function get-request of Axios
export const getRequest = a => dispatch => {
return axios
.get(a)
.then(function(response) {
dispatch({
type: FETCH_DATA,
payload: response.data
});
})
.catch(function(error) {
dispatch({ type: ERROR_DATA, payload: { status: error.response.status, statusText: error.response.statusText } });
});
};
thanks in advance :)
Here is the solution:
index.ts:
import axios from 'axios';
export const FETCH_DATA = 'FETCH_DATA';
export const ERROR_DATA = 'ERROR_DATA';
export const getRequest = a => dispatch => {
return axios
.get(a)
.then(response => {
dispatch({
type: FETCH_DATA,
payload: response.data
});
})
.catch(error => {
dispatch({ type: ERROR_DATA, payload: { status: error.response.status, statusText: error.response.statusText } });
});
};
index.spec.ts:
import axios from 'axios';
import { getRequest, FETCH_DATA, ERROR_DATA } from './';
describe('getRequest', () => {
const dispatch = jest.fn();
it('should get data and dispatch action correctly', async () => {
const axiosGetSpyOn = jest.spyOn(axios, 'get').mockResolvedValueOnce({ data: 'mocked data' });
await getRequest('jest')(dispatch);
expect(axiosGetSpyOn).toBeCalledWith('jest');
expect(dispatch).toBeCalledWith({ type: FETCH_DATA, payload: 'mocked data' });
axiosGetSpyOn.mockRestore();
});
it('should dispatch error', async () => {
const error = {
response: {
status: 400,
statusText: 'client error'
}
};
const axiosGetSpyOn = jest.spyOn(axios, 'get').mockRejectedValueOnce(error);
await getRequest('ts')(dispatch);
expect(axiosGetSpyOn).toBeCalledWith('ts');
expect(dispatch).toBeCalledWith({ type: ERROR_DATA, payload: error.response });
axiosGetSpyOn.mockRestore();
});
});
Unit test result and coverage:
PASS 45062447/index.spec.ts
getRequest
✓ should get data and dispatch action correctly (9ms)
✓ should dispatch error (2ms)
----------|----------|----------|----------|----------|-------------------|
File | % Stmts | % Branch | % Funcs | % Lines | Uncovered Line #s |
----------|----------|----------|----------|----------|-------------------|
All files | 100 | 100 | 100 | 100 | |
index.ts | 100 | 100 | 100 | 100 | |
----------|----------|----------|----------|----------|-------------------|
Test Suites: 1 passed, 1 total
Tests: 2 passed, 2 total
Snapshots: 0 total
Time: 1.551s, estimated 3s
Here is the code: https://github.com/mrdulin/jest-codelab/tree/master/src/stackoverflow/45062447