I have following axios get code which uses transformResponse
const downloadAttachment = ({attachmentId, id, oId}) => {
return axios.get(`${url}/attachments/file/${attachmentId}/v1`, {
headers: buildHeaders(),
params: getUrlSearchParams({id, oId}),
responseType: 'blob',
timeout: REQUEST_TIMEOUT,
transformResponse: [(data) => ({file: data})],
});
};
I am using JEST for unit testing. I could get unit test passed with following code
it('should call milo with correct arguments', () => {
expect(axios.get).toHaveBeenCalledWith(mockdownloadAttachmentUrl, {
headers: mockHeaders,
params: {
id: 'ABCD',
oId: 'XYZ',
},
responseType: 'blob',
timeout: 5000,
transformResponse: [expect.any(Function)],
});
});
However JEST is not able to complete 100% code coverage and pointing to line transformResponse: [(data) => ({file: data})] in code which is not covered. How to write test to cover this part of code? Please advice.
If you just want to increase coverage and keep it simple, you can use the following way:
You can use .mockImplementationOnce() to mock axios.get() method, then you can get the transformResponse option in your test case. You can test it as usual.
index.ts:
import axios from 'axios';
const url = 'http://localhost:3000/api';
export const downloadAttachment = ({ attachmentId, id, oId }) => {
return axios.get(`${url}/attachments/file/${attachmentId}/v1`, {
responseType: 'blob',
transformResponse: [(data) => ({ file: data })],
});
};
index.test.ts:
import { downloadAttachment } from './';
import axios from 'axios';
describe('66579132', () => {
it('should transform response', async () => {
const mRes = {};
let transformResponse;
jest.spyOn(axios, 'get').mockImplementationOnce((url, options) => {
transformResponse = options!.transformResponse![0];
return Promise.resolve(mRes);
});
await downloadAttachment({ attachmentId: 1, id: 1, oId: 1 });
expect(axios.get).toBeCalled();
// test tranfromResponse
const response = transformResponse('teresa teng');
expect(response).toEqual({ file: 'teresa teng' });
});
});
unit test result:
PASS examples/66579132/index.test.ts
66579132
✓ should transform response (4 ms)
----------|---------|----------|---------|---------|-------------------
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: 4.393 s
Related
I am trying to write test cases for my API call function and I don't know where is the error that I could not run my test successfully here is the API call function Code and test Cases code.
export async function getUserTest() {
fetch(config.apiUrl.myFleetAPI, {
method: 'GET',
headers: {
Accept: 'application/json',
'Content-Type': 'application/json',
Authorization: 'Bearer ' + 'GcGs5OF5TQ50sbjXRXDDtG8APTSa0s'
}
})
.then((response) => {
return response.json();
})
.catch((reject) => console.log(reject));
}
Test Case Code .
import React from 'react';
import { getUserTest } from '../Service/Dashboard/Dashboard';
global.fetch = jest.fn();
const mockAPICall = (option, data) => global.fetch.mockImplementation(() => Promise[option](data));
describe('Car Components component', () => {
describe('when rendered', () => {
it('should call a fetchData function', async () => {
const testData = { current_user: 'Rahul Raj', name: 'Lafarge' };
mockAPICall('resolve', testData);
return getUserTest().then((data) => {
expect(data).toEqual(testData);
});
});
});
});
and here is what I am getting an error when I tried to run the Test Cases.
Car Components component
when rendered
✕ should call a fetchData function (5 ms)
● Car Components component › when rendered › should call a fetchData function
expect(received).toEqual(expected) // deep equality
Expected: {"current_user": "Rahul Raj", "name": "Lafarge"}
Received: undefined
65 | mockAPICall('resolve', testData);
66 | return getUserTest().then((data) => {
> 67 | expect(data).toEqual(testData);
| ^
68 | });
69 | });
70 | });
at src/Test/MainScreen.test.js:67:30
console.log
TypeError: response.json is not a function
at /Users/rahulraj/Documents/Workproject/Vivafront/lafargeClone/src/Service/Dashboard/Dashboard.js:44:29
at processTicksAndRejections (internal/process/task_queues.js:93:5)
There are two problems with your code:
The resolved value of fetch function is Response which has a .json() method, you forgot to mock it.
You forgot to return the promise like return fetch(/.../). This will cause the promise chain in the test case to not wait for the fetch promise to complete, which is why the return result is undefined
E.g.
api.js:
const config = {
apiUrl: {
myFleetAPI: 'http://localhost:3000/api',
},
};
export async function getUserTest() {
return fetch(config.apiUrl.myFleetAPI, {
method: 'GET',
headers: {
Accept: 'application/json',
'Content-Type': 'application/json',
Authorization: 'Bearer ' + 'GcGs5OF5TQ50sbjXRXDDtG8APTSa0s',
},
})
.then((response) => {
return response.json();
})
.catch((reject) => console.log(reject));
}
api.test.js:
import { getUserTest } from './api';
describe('68771600', () => {
test('should pass', () => {
const testData = { current_user: 'Rahul Raj', name: 'Lafarge' };
const response = { json: jest.fn().mockResolvedValueOnce(testData) };
global.fetch = jest.fn().mockResolvedValueOnce(response);
return getUserTest().then((data) => {
expect(data).toEqual(testData);
});
});
});
test result:
PASS examples/68771600/api.test.js (9.885 s)
68771600
✓ should pass (3 ms)
----------|---------|----------|---------|---------|-------------------
File | % Stmts | % Branch | % Funcs | % Lines | Uncovered Line #s
----------|---------|----------|---------|---------|-------------------
All files | 80 | 100 | 66.67 | 80 |
api.js | 80 | 100 | 66.67 | 80 | 18
----------|---------|----------|---------|---------|-------------------
Test Suites: 1 passed, 1 total
Tests: 1 passed, 1 total
Snapshots: 0 total
Time: 10.794 s
How to mock the call of service.request in the code bellow?
import url from 'url'
import jayson from 'jayson/promise'
export async function dispatch(webHook, method, payload) {
const service = jayson.Client.https({ ...url.parse(webHook) })
return service.request(method, { ...payload })
}
In my unit-test I want to do something like this
jest.mock("") // what should go here?
it(() => {
const method = 'test'
expect(request).toHaveBeenCalledWith(method...) ?
})
UPDATE
I updated with my findings my code, but still no luck
import { Client } from 'jayson/promise'
import { dispatch } from '../src/remote'
jest.mock('jayson')
describe('remote', () => {
let spy: jest.SpyInstance<any>
beforeEach(() => {
spy = jest.spyOn(Client.https.prototype, 'request')
})
afterEach(() => {
spy.mockClear()
})
it('should invoke request method', () => {
const url = 'http://example.com:8000'
const method = ''
const payload = {}
dispatch(url, method, payload)
expect(spy).toHaveBeenCalledWith({})
})
})
You can use jest.mock to mock jayson/promise module. Don't need to use jest.spyOn.
E.g.
index.ts:
import url from 'url';
import jayson from 'jayson/promise';
export async function dispatch(webHook, method, payload) {
const service = jayson.Client.https({ ...url.parse(webHook) });
return service.request(method, { ...payload });
}
index.test.ts:
import { dispatch } from './';
import jayson, { HttpsClient } from 'jayson/promise';
import { mocked } from 'ts-jest';
jest.mock('jayson/promise');
const httpsClientMock = mocked(jayson.Client.https);
describe('65924278', () => {
afterAll(() => {
jest.resetAllMocks();
});
it('should pass', async () => {
const url = 'http://example.com:8000';
const method = '';
const payload = {};
const serviceMock = ({
request: jest.fn(),
} as unknown) as HttpsClient;
httpsClientMock.mockReturnValueOnce(serviceMock);
await dispatch(url, method, payload);
expect(jayson.Client.https).toBeCalledTimes(1);
expect(serviceMock.request).toBeCalledWith('', {});
});
});
unit test result:
PASS examples/65924278/index.test.ts (10.748 s)
65924278
√ should pass (13 ms)
----------|---------|----------|---------|---------|-------------------
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: 13.15 s
I am trying to unit test a function which sends a post request then the API returns a json object. I am trying to do this using jest and fetch-mock-jest.
Now instead of the expected payload the tested function receives {"size":0,"timeout":0}
and throws error invalid json response body at reason: Unexpected end of JSON input. Pretty sure there is something basic I don't see. I spent way more time on this without any progress than I'd like to admit.
Edit: I am pretty new to jest and unit testing in general, so if someone has a better suggestion to go about mocking fetch, please tell me.
Test File
import fetchMock from 'fetch-mock-jest'
import {
refreshAccessToken, isTokenExpired
} from '../../../lib/access/AccessToken'
describe('AccessToken Component...', () => {
it('...Refreshes AccessToken', async () => {
const responseBody = { accessToken: taiDeveloperTokenValid } // The payload I want the AccessToken.refreshAccessTokento get
setAccessToken(taiDeveloperTokenExpired)
process.env.NEXT_PUBLIC_AUTH_API_HTTPS_URL = 'http://new-api.com'
fetchMock.post(
`${process.env.NEXT_PUBLIC_AUTH_API_HTTPS_URL}/refreshToken`,
new Promise((res) =>
setTimeout(
() =>
res({
status: 200,
body: JSON.stringify(responseBody),
statusText: 'OK',
headers: { 'Content-Type': 'application/json' },
}),
50,
),
),
)
const refreshAccessTokenResponse = await refreshAccessToken()
expect(refreshAccessTokenResponse).toBe(true)
expect(isTokenExpired()).toBe(false)
})
}
Function I am testing
import fetch from 'isomorphic-unfetch'
export const refreshAccessToken = async (): Promise<boolean> => {
try {
const response = await fetch(
`${process.env.NEXT_PUBLIC_AUTH_API_HTTPS_URL}/refreshToken`,
{
method: 'POST',
credentials: 'include',
},
)
console.log(JSON.stringify(await response)) // this prints {"size":0,"timeout":0}
const data = await response.json()
accessToken = data.accessToken
return true
} catch (error) {
console.log(error) // this prints FetchError { message: 'invalid json response body at reason: Unexpected end of JSON input', type: 'invalid-json'
return false
}
}
You can use jest.mock(moduleName, factory, options) to mock isomorphic-unfetch module by yourself. Don't need fetch-mock-jest module.
E.g.
main.ts:
import fetch from 'isomorphic-unfetch';
export const refreshAccessToken = async (): Promise<boolean> => {
try {
const response = await fetch(`${process.env.NEXT_PUBLIC_AUTH_API_HTTPS_URL}/refreshToken`, {
method: 'POST',
credentials: 'include',
});
const data = await response.json();
const accessToken = data.accessToken;
console.log(accessToken);
return true;
} catch (error) {
console.log(error);
return false;
}
};
main.test.ts:
import { refreshAccessToken } from './main';
import fetch from 'isomorphic-unfetch';
import { mocked } from 'ts-jest/utils';
jest.mock('isomorphic-unfetch');
const fetchMocked = mocked(fetch);
describe('66009798', () => {
afterAll(() => {
jest.resetAllMocks();
});
it('should get access token', async () => {
process.env.NEXT_PUBLIC_AUTH_API_HTTPS_URL = 'http://new-api.com';
const data = { accessToken: '123' };
const mResponse = { json: jest.fn().mockResolvedValueOnce(data) };
fetchMocked.mockResolvedValueOnce(mResponse as any);
const actual = await refreshAccessToken();
expect(actual).toBeTruthy();
expect(fetch).toBeCalledWith('http://new-api.com/refreshToken', {
method: 'POST',
credentials: 'include',
});
expect(mResponse.json).toBeCalledTimes(1);
});
});
unit test result:
PASS examples/66009798/main.test.ts (13.604 s)
66009798
√ should get access token (37 ms)
console.log
123
at examples/66009798/main.ts:11:13
----------|---------|----------|---------|---------|-------------------
File | % Stmts | % Branch | % Funcs | % Lines | Uncovered Line #s
----------|---------|----------|---------|---------|-------------------
All files | 83.33 | 100 | 100 | 80 |
main.ts | 83.33 | 100 | 100 | 80 | 14-15
----------|---------|----------|---------|---------|-------------------
Test Suites: 1 passed, 1 total
Tests: 1 passed, 1 total
Snapshots: 0 total
Time: 15.989 s
export const get = () => {
return fetch().then((response) => funcA(response));
};
const funcA = (response) => {
if (!response.ok) {
return response.json().then((data) => {
throw Error(...);
});
}
};
How can i mock response.json().then() ? I got the error response.json(...).then is not a function.
I put the json() in the response i mocked
response = { ok: false, json: () => err };
response.json method should return a promise. E.g.
index.ts:
export const funcA = (response) => {
if (!response.ok) {
return response.json().then((data) => {
throw Error(data);
});
}
};
index.test.ts:
import { funcA } from './';
describe('48916842', () => {
it('should pass', () => {
const mResponse = { ok: false, json: jest.fn().mockRejectedValueOnce('custom error') };
expect(funcA(mResponse)).rejects.toThrowError('custom error');
});
});
unit test result with coverage report:
PASS stackoverflow/48916842/index.test.ts (10.276s)
48916842
✓ should pass (5ms)
----------|---------|----------|---------|---------|-------------------
File | % Stmts | % Branch | % Funcs | % Lines | Uncovered Line #s
----------|---------|----------|---------|---------|-------------------
All files | 75 | 50 | 50 | 75 |
index.ts | 75 | 50 | 50 | 75 | 4
----------|---------|----------|---------|---------|-------------------
Test Suites: 1 passed, 1 total
Tests: 1 passed, 1 total
Snapshots: 0 total
Time: 11.929s, estimated 12s
I am using Auth0 to manage authentication for my React App. Here's the function that I want to test:
login(username: string, password: string) {
return new Promise((resolve, reject) => {
this.auth0.client.login({
realm: 'Username-Password-Authentication',
username,
password,
}, (err, authResult) => {
if (err) {
reject(err);
}
if (authResult && authResult.idToken && authResult.accessToken) {
resolve(authResult.idToken);
}
});
});
}
auth0 is instantiated in the following manner:
constructor(clientID: string, domain: string, redirectUri: string) {
// Configure auth0
this.auth0 = new auth0.WebAuth({
clientID,
domain,
responseType: 'token id_token',
redirectUri,
});
}
I have the following two tests:
I want to see if I can instantiate the AuthService class.
I want to see if I can login using a username/password combination
Here's the test file that I wrote:
jest.unmock('../AuthService');
import AuthService from '../AuthService';
describe('Auth0 Library', () => {
test('Should be able to instantiate', () => {
const auth0 = new AuthService('clientID', 'domain');
expect(auth0).toEqual(expect.anything());
});
});
describe('Auth0 Login', () => {
test('Fetch token for existing user', () => {
const auth0 = new AuthService('clientID', 'domain');
auth0.login('email', 'pw')
.then((idToken) => {
console.log('idToken ', idToken);
expect(auth0).toEqual(expect.anything());
});
});
});
The first test runs as expected. However, the second test never works. The promise that is returned is seemingly never resolved, and I never see the console.log.
Could someone please explain to me what I am doing wrong? I am fairly new when it comes to writing jest tests.
Here is the unit test solution for using Promise and error-first callback together.
Folder structure:
.
├── AuthService.spec.ts
├── AuthService.ts
└── auth0
├── WebAuth.ts
└── index.ts
1 directory, 4 files
E.g.
AuthService.ts:
import * as auth0 from './auth0';
export class AuthService {
private auth0: any;
constructor(clientID: string, domain: string, redirectUri: string) {
this.auth0 = new auth0.WebAuth({
clientID,
domain,
responseType: 'token id_token',
redirectUri
});
}
public login(username: string, password: string) {
return new Promise((resolve, reject) => {
this.auth0.client.login(
{
realm: 'Username-Password-Authentication',
username,
password
},
(err, authResult) => {
if (err) {
reject(err);
}
if (authResult && authResult.idToken && authResult.accessToken) {
resolve(authResult.idToken);
}
}
);
});
}
}
auth0/WebAuth.ts:
export class WebAuth {
public client = {
login(options, callback) {
callback(null, {});
}
};
constructor(...args: any) {}
}
auth0/index.ts:
export * from './WebAuth';
AuthService.spec.ts:
import { AuthService } from './AuthService';
const authService = new AuthService('clientid', 'domain', 'redirectUri');
describe('AuthService', () => {
describe('#login', () => {
afterEach(() => {
jest.restoreAllMocks();
});
it('should login successfully and return id token', async done => {
let loginCallback;
jest.spyOn(authService['auth0']['client'], 'login').mockImplementation((options, callback) => {
loginCallback = callback;
done();
});
const actualValue = authService.login('username', 'password');
const mAuthResult = { idToken: '123', accessToken: '321' };
loginCallback(null, mAuthResult);
await expect(actualValue).resolves.toBe('123');
});
it('should login failed', async done => {
let loginCallback;
jest.spyOn(authService['auth0']['client'], 'login').mockImplementation((options, callback) => {
loginCallback = callback;
done();
});
const actualValue = authService.login('username', 'password');
const mError = new Error('network error');
loginCallback(mError, null);
await expect(actualValue).rejects.toThrowError(mError);
});
});
});
Unit test result with 100% coverage for AuthService.ts:
PASS src/stackoverflow/42137891/AuthService.spec.ts
AuthService
#login
✓ should login successfully and return id token (5ms)
✓ should login failed (2ms)
---------------------|----------|----------|----------|----------|-------------------|
File | % Stmts | % Branch | % Funcs | % Lines | Uncovered Line #s |
---------------------|----------|----------|----------|----------|-------------------|
All files | 95 | 100 | 87.5 | 94.12 | |
42137891-todo | 100 | 100 | 100 | 100 | |
AuthService.ts | 100 | 100 | 100 | 100 | |
42137891-todo/auth0 | 85.71 | 100 | 66.67 | 83.33 | |
WebAuth.ts | 83.33 | 100 | 66.67 | 80 | 4 |
index.ts | 100 | 100 | 100 | 100 | |
---------------------|----------|----------|----------|----------|-------------------|
Test Suites: 1 passed, 1 total
Tests: 2 passed, 2 total
Snapshots: 0 total
Time: 3.728s
Source code: https://github.com/mrdulin/jest-codelab/tree/master/src/stackoverflow/42137891