Using expect.any() with supertest to check response body - unit-testing

I'm trying to use supertest to check res.body with Jest, but the following snippet will always fail
request(app)
.post('/auth/signup')
.send(validEmailSample)
.expect(200, {
success: true,
message: 'registration success',
token: expect.any(String),
user: expect.any(Object),
});
But when I rewrite the test to check the body in a callback as follows:
test('valid userData + valid email will result in registration sucess(200) with message object.', (done) => {
request(app)
.post('/auth/signup')
.send(validEmailSample)
.expect(200)
.end((err, res) => {
if (err) done(err);
expect(res.body.success).toEqual(true);
expect(res.body.message).toEqual('registration successful');
expect(res.body.token).toEqual(expect.any(String));
expect(res.body.user).toEqual(expect.any(Object));
expect.assertions(4);
done();
});
});
The test will pass.
I'm sure it has something to do with expect.any(). As Jest's documentation says that expect.any and expect.anything can only be used together with expect().toEqual, and expect().toHaveBeenCalledWith()
I'm wondering if there's any better way to do it, to use expect.any in supertest's expect api.

You can use expect.objectContaining(object).
matches any received object that recursively matches the expected properties. That is, the expected object is a subset of the received object. Therefore, it matches a received object which contains properties that are present in the expected object.
app.js:
const express = require("express");
const app = express();
app.post("/auth/signup", (req, res) => {
const data = {
success: true,
message: "registration success",
token: "123",
user: {},
};
res.json(data);
});
module.exports = app;
app.test.js:
const app = require('./app');
const request = require('supertest');
describe('47865190', () => {
it('should pass', (done) => {
expect.assertions(1);
request(app)
.post('/auth/signup')
.expect(200)
.end((err, res) => {
if (err) return done(err);
expect(res.body).toEqual(
expect.objectContaining({
success: true,
message: 'registration success',
token: expect.any(String),
user: expect.any(Object),
}),
);
done();
});
});
});
Integration test result with coverage report:
PASS src/stackoverflow/47865190/app.test.js (12.857s)
47865190
✓ should pass (48ms)
----------|----------|----------|----------|----------|-------------------|
File | % Stmts | % Branch | % Funcs | % Lines | Uncovered Line #s |
----------|----------|----------|----------|----------|-------------------|
All files | 100 | 100 | 100 | 100 | |
app.js | 100 | 100 | 100 | 100 | |
----------|----------|----------|----------|----------|-------------------|
Test Suites: 1 passed, 1 total
Tests: 1 passed, 1 total
Snapshots: 0 total
Time: 14.319s
Source code: https://github.com/mrdulin/jest-codelab/tree/master/src/stackoverflow/47865190

Related

Jest Mock nor working as expected throws an error

const axios = "axios";
jest.mock(axios);
axios.get.mockImplementation((url) => {
if(url === process.env.ENHANCED_CLAIM_STATUS_276_DETAILS){
return Promise.resolve({ status: 200, data: claim_response_276 });
}
if(url === process.env.ENHANCED_VALUE_ADDS_277_DETAILS){
return Promise.resolve({ status: 200, data: claim_response_277 });
}
});
i am trying to mock the api responses but it throws this error :
**TypeError: Cannot read properties of undefined (reading 'mockImplementation')**
The moduleName parameter should be a string. See API doc jest.mock(moduleName, factory, options)
An working example:
import axios from "axios";
jest.mock('axios');
describe('74929332', () => {
test('should pass', async () => {
axios.get.mockImplementation((url) => {
return Promise.resolve({ status: 200, data: 'claim_response_276' });
});
const result = await axios.get('http://localhost:3000/api')
expect(result).toEqual({ status: 200, data: 'claim_response_276' })
})
});
Test result:
PASS stackoverflow/74929332/index.test.js (14.419 s)
74929332
✓ should pass (3 ms)
Test Suites: 1 passed, 1 total
Tests: 1 passed, 1 total
Snapshots: 0 total
Time: 15.559 s

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');

Test self-executing function wit Jest

I understand reaching 100% unit test coverage shouldn't be a goal. I have actually excluded index.js from the coverage tests, I'm just interested to know how it would be done.
How can the self-executing function in index.js be tested? I'd like to test that it calls scrape.
index.js:
import scrape from './scrape';
const main = (async () => {
await scrape();
})();
export default main;
What I have tried:
import main from './index';
const scrape = jest.fn();
describe('On the index file', () => {
it('scrape executes automatically', async () => {
await main();
expect(scrape).toHaveBeenCalled();
});
});
This errors with TypeError: (0 , _index.default) is not a function
I see that main isn't a function but I haven't managed to make it work.
Here is my test strategy, you can use jest.mock(moduleName, factory, options) method mock the scrape module.
index.ts:
import scrape from './scrape';
const main = (async () => {
return await scrape();
})();
export default main;
index.spec.ts:
import scrape from './scrape';
import main from './';
jest.mock('./scrape.ts', () => jest.fn().mockResolvedValueOnce('fake data'));
describe('main', () => {
test('should return data', async () => {
const actualValue = await main;
expect(actualValue).toBe('fake data');
expect(scrape).toBeCalledTimes(1);
});
});
Unit test result with 100% coverage:
PASS src/stackoverflow/58674975/index.spec.ts (7.434s)
main
✓ should return data (5ms)
----------|----------|----------|----------|----------|-------------------|
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: 1 passed, 1 total
Snapshots: 0 total
Time: 8.684s
Source code: https://github.com/mrdulin/jest-codelab/tree/master/src/stackoverflow/58674975

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