I tried following:
jest.mock('axios', () => jest.fn(() => Promise.resolve({ data: testData })));
also tried adding __mocks__/axios.ts:
export default {
default: jest.fn(),
request: jest.fn(),
};
but it returns:
TypeError: Cannot read property 'request' of undefined
7 | const CLIENT_ROUTER_REQUIRED_HEADER = { 'Content-Type': 'application/json' };
8 |
> 9 | axiosRetry(axios, { retries: 3 });
| ^
10 |
11 | const responseData = axios({
12 | baseURL: baseUrl ? baseUrl : '',
AxiosService.ts
import axios, { AxiosResponse } from 'axios';
import axiosRetry from 'axios-retry';
export const axiosRequest = (data: object, baseUrl?: string): Object => {
const CLIENT_ROUTER_END_POINT = '/client-router';
const CLIENT_ROUTER_HTTP_METHOD = 'POST';
const CLIENT_ROUTER_REQUIRED_HEADER = { 'Content-Type': 'application/json' };
axiosRetry(axios, { retries: 3 });
const responseData = axios({
baseURL: baseUrl ? baseUrl : '',
url: CLIENT_ROUTER_END_POINT,
method: CLIENT_ROUTER_HTTP_METHOD,
headers: CLIENT_ROUTER_REQUIRED_HEADER,
data: data,
})
.then(function (response: AxiosResponse) {
return response.data;
})
.catch((e) => {
return JSON.stringify(e);
});
return responseData;
};
index.ts
import { axiosRequest } from './AxiosService';
export const retrieveDataFromServer = async (
httpMethod: string,
gatewayPath: string,
requestParameters: object,
baseUrl?: string
): Promise<Object> => {
const data = {
httpMethod: httpMethod,
gatewayPath: gatewayPath,
requestParameters: requestParameters,
};
const responseData = axiosRequest(data, baseUrl);
return responseData;
};
index.test.ts
import { retrieveDataFromServer } from '../src';
describe('Frontend Client Router React Component', () => {
test('Retrieve data from job-search endpoint', async () => {
// The purpose of this test is to show an example on how to use retrieveDataFromServer()
const data = {
query: 'strawberry',
//...other data
};
const testData = {
responseBody:
'["1", "2", "3"]',
responseCode: 200,
};
jest.mock('axios', () => jest.fn(() => Promise.resolve({ data: testData })));
expect(
await retrieveDataFromServer(
'GET',
'/search',
data,
'http://localhost:8881/'
)
).toMatchObject(testData);
});
});
I ended up adding:
import axios from 'axios';
import MockAdapter from 'axios-mock-adapter';
const mock = new MockAdapter(axios);
test('Retrieve data from autocomplete endpoint', async () => {
const data: AutocompleteData = {
query: 'strawberry',
};
const testData = [
'strawberry',
];
mock.onPost().replyOnce(200, {
testData,
});
await expect(autocomplete(AutocompleteType.where, data)).resolves.toEqual({
testData: testData,
});
}
to my test code.
Related
i am trying to use jest with nextJS to test API. I am using a custom interceptor for all http request to have authorization token on header. Here is my interceptor code
Api.ts
import axios from 'axios';
import config from '../config/index';
const Api = () => {
const defaultOptions = {
baseURL: config.APIENDPOINT,
method: 'get',
headers: {
'Content-Type': 'application/json',
},
};
// Create instance
let instance = axios.create(defaultOptions);
// Set the AUTH token for any request
instance.interceptors.request.use((config) => {
const token = localStorage.getItem('token');
//#ts-ignore
config.headers.Authorization = token ? `${token}` : '';
return config;
});
instance.interceptors.response.use((res) => {
return res
});
return instance;
};
export default Api();
Here is the code to call the API
export const loadMerchants = async (id: any) => {
const data = await Api.get(config.APIENDPOINT + "/merchants/company/" + id)
console.log("data" ,data);
return (data)
}
And here is my test code
const axios = require('axios');
jest.mock('axios', () => {
return {
get: jest.fn(),
create: jest.fn(() => ({
interceptors: {
request: { use: jest.fn(() => Promise.resolve({ data: { foo: 'bar' } })) },
response: { use: jest.fn(() => Promise.resolve({ data: { foo: 'bar' } })) },
}
}))
}
})
it('Merchant API call', async () => {
axios.get.mockResolvedValue({
data: [
{
userId: 1,
id: 1,
title: 'My First Album'
},
{
userId: 1,
id: 2,
title: 'Album: The Sequel'
}
]
});
const merchants = await loadMerchants("1")
console.log(merchants) //always undefined
// expect(merchants).toEqual('some data');
});
on my API call if use axios.get instead of Api.get i get the correct results. I have looked into google and haven`t found any solutions.
Any help would be appreciated. Thank you.
Here is my sample src/main.ts file
import axios from 'axios';
export async function main() {
const URL = 'test url';
const secretKey = 'Test key'
const response = await axios.get(URL, {
headers: { 'Content-Type': 'application/json', 'KEY': secretKey },
});
I want to write my test case in spec/test.ts file using mocha. Can someone show me how to create a mock and stub for axios dependency.
For mock/stub axios in typestript I recommend axios-mock-adapter, for expect functions chai
Here is an example of how to do this
request.ts
import axios from 'axios';
const apiConfig = {
returnRejectedPromiseOnError: true,
timeout: 30000,
headers: {
common: {
'Content-Type': 'application/json',
'Accept': 'application/json',
},
},
};
const request = axios.create(apiConfig);
export default request;
main.ts
import request from './request';
export const URL = 'https://httpbin.org/get';
export const secretKey = 'secret_key';
export async function main() {
const response = await request.get(URL, {
headers: {
KEY: secretKey,
},
});
// response logic
return response;
}
main.spec.ts
import MockAdapter from 'axios-mock-adapter';
import { expect } from 'chai';
import request from './request';
import { main, URL, secretKey } from './main';
describe('Request test', () => {
let stub: MockAdapter;
const receivedData = { data: 'data' };
before(() => {
stub = new MockAdapter(request);
stub.onGet(URL, {
headers: {
KEY: secretKey,
},
}).replyOnce(200, receivedData);
// replyOnce if you assume that your code sends a single request
});
it('test', async () => {
const response = await main();
expect(response.status).to.be.equal(200);
expect(response.data).to.be.deep.equal(receivedData);
});
after(() => {
stub.restore();
});
});
I am struggling with how to mock below functionality. I need to mock both methods: getAllBookInCategory, deleteBookInCategory
The public method calls private methods and I am assuming I don't have to test private method, only calling public methods and verifying private methods get called. Is it correct?
public methods: getAllBookInCategory, deleteBookInCategory
private method: makeRequest
import rp from "request-promise-native";
export class BookService {
#param bookCategory id of book category
#param boooks list of books
public static async getAllBookInCategory(bookCategory: string) {
try {
const neededInfo = {
url: `https://${process.env.BOOK_HOST}/bookapi/${process.env.BOOKAPI_VERSION}/iterative/bookCategory/${ bookCategory }/books/all `,
method: 'GET',
}
const result = await BookService.makeRequest(bookCategory, neededInfo);
return rp(result);
} catch(error) {
Console.log(`Failed to get All Books in given category ${error}`)
}
}
public static async deleteBookInCategory(bookCategory: string, books: string[]) {
try{
const neededInfo = {
url: `https://${process.env.BOOK_HOST}/bookapi/${process.env.BOOKAPI_VERSION}/ iterative /bookCategory/${ bookCategory }/books/bookDelete?books=${books.join()}`,
method: 'DELETE',
}
const result = await BookService.makeRequest(bookCategory, neededInfo);
return rp(result);
} catch(error) {
Console.log(`Failed to delete books from category: ${error}`)
}
}
private static async makeRequest(bookCategory: string, neededInfo: any, bodydata?: any) {
const authValue = await BookService.getAuthValue(bookCategory, neededInfo);
return {
method: neededInfo.method,
url: neededInfo.url,
headers: {
Host: process.env.BOOK_HOST,
Authorization: authValue,
},
body: bodydata,
json: true
};
}
}
Here is the unit test solution for getAllBookInCategory method. You code has an issue, you should use return await rp(result) instead of using return rp(result).
BookService.ts:
import rp from 'request-promise-native';
export class BookService {
public static async getAllBookInCategory(bookCategory: string) {
try {
const neededInfo = {
url: `https://${process.env.BOOK_HOST}/bookapi/${process.env.BOOKAPI_VERSION}/iterative/bookCategory/${bookCategory}/books/all`,
method: 'GET',
};
const result = await BookService.makeRequest(bookCategory, neededInfo);
return await rp(result);
} catch (error) {
console.log(`Failed to get All Books in given category ${error}`);
}
}
public static async deleteBookInCategory(bookCategory: string, books: string[]) {
try {
const neededInfo = {
url: `https://${process.env.BOOK_HOST}/bookapi/${
process.env.BOOKAPI_VERSION
}/iterative/bookCategory/${bookCategory}/books/bookDelete?books=${books.join()}`,
method: 'DELETE',
};
const result = await BookService.makeRequest(bookCategory, neededInfo);
return rp(result);
} catch (error) {
console.log(`Failed to delete books from category: ${error}`);
}
}
private static async makeRequest(bookCategory: string, neededInfo: any, bodydata?: any) {
const authValue = await BookService.getAuthValue(bookCategory, neededInfo);
return {
method: neededInfo.method,
url: neededInfo.url,
headers: {
Host: process.env.BOOK_HOST,
Authorization: authValue,
},
body: bodydata,
json: true,
};
}
private static async getAuthValue(catetory, info) {
return 'authvalue';
}
}
BookService.test.ts:
import { BookService } from './BookService';
import rp from 'request-promise-native';
jest.mock('request-promise-native', () => jest.fn());
describe('BookService', () => {
afterEach(() => {
jest.resetAllMocks();
jest.restoreAllMocks();
});
describe('#getAllBookInCategory', () => {
beforeEach(() => {
process.env.BOOK_HOST = 'example.com';
process.env.BOOKAPI_VERSION = 'v1';
});
afterEach(() => {
process.env.BOOK_HOST = '';
process.env.BOOKAPI_VERSION = '';
});
it('should make request correctly', async () => {
const mAuthvalue = 'mocked auth value';
jest.spyOn(BookService as any, 'getAuthValue').mockResolvedValueOnce(mAuthvalue);
const mResponse = 'success';
rp.mockResolvedValueOnce(mResponse);
const actual = await BookService.getAllBookInCategory('programming');
expect(actual).toEqual(mResponse);
expect(rp).toBeCalledWith({
method: 'GET',
url: 'https://example.com/bookapi/v1/iterative/bookCategory/programming/books/all',
headers: {
Host: 'example.com',
Authorization: mAuthvalue,
},
body: undefined,
json: true,
});
});
it('should print an error when make request failed', async () => {
const mAuthvalue = 'mocked auth value';
jest.spyOn(BookService as any, 'getAuthValue').mockResolvedValueOnce(mAuthvalue);
const logSpy = jest.spyOn(console, 'log');
const mError = new Error('Internal server error');
rp.mockRejectedValueOnce(mError);
await BookService.getAllBookInCategory('programming');
expect(rp).toBeCalledWith({
method: 'GET',
url: 'https://example.com/bookapi/v1/iterative/bookCategory/programming/books/all',
headers: {
Host: 'example.com',
Authorization: mAuthvalue,
},
body: undefined,
json: true,
});
expect(logSpy).toBeCalledWith(`Failed to get All Books in given category ${mError}`);
});
});
});
Unit test result with coverage report:
PASS src/stackoverflow/59315311/BookService.test.ts
BookService
#getAllBookInCategory
✓ should make request correctly (9ms)
✓ should print an error when make request failed (8ms)
console.log node_modules/jest-mock/build/index.js:860
Failed to get All Books in given category Error: Internal server error
----------------|----------|----------|----------|----------|-------------------|
File | % Stmts | % Branch | % Funcs | % Lines | Uncovered Line #s |
----------------|----------|----------|----------|----------|-------------------|
All files | 73.91 | 100 | 60 | 72.22 | |
BookService.ts | 73.91 | 100 | 60 | 72.22 | 21,28,30,32,52 |
----------------|----------|----------|----------|----------|-------------------|
Test Suites: 1 passed, 1 total
Tests: 2 passed, 2 total
Snapshots: 0 total
Time: 5.662s, estimated 13s
You can test deleteBookInCategory method in a same way.
Source code: https://github.com/mrdulin/jest-codelab/tree/master/src/stackoverflow/59315311
I am writing tests for my asynchronous actions. I have abstracted away my axios calls into a separate class. If I want to test my asynchronous redux action, how do I write a mock for api.js so that sampleAction.test.js will pass? Thanks!
api.js:
import axios from 'axios';
let apiUrl = '/api/';
if (process.env.NODE_ENV === 'test') {
apiUrl = 'http://localhost:8080/api/';
}
export default class Api {
static async get(url) {
const response = await axios.get(`${apiUrl}${url}`, {withCredentials: true});
return response;
}
}
sampleAction.js:
import Api from './api';
export const fetchData = () => async (dispatch) => {
try {
const response = await Api.get('foo');
dispatch({
type: 'RECEIVE_DATA',
data: response.data.data,
});
} catch (error) {
handleError(error);
}
};
sampleAction.test.js:
import store from './store';
test('testing RECEIVE_DATA async action', () => {
const expectedActions = [
{ type: 'RECEIVE_DATA', data: 'payload' },
];
return store.dispatch(actions.fetchData()).then(() => {
expect(store.getActions()).toEqual(expectedActions);
});
});
You can mock Api.get like this:
import { fetchData } from './sampleAction';
import Api from './api';
let getMock;
beforeEach(() => {
getMock = jest.spyOn(Api, 'get');
getMock.mockResolvedValue({ data: { data: 'mock data' } });
});
afterEach(() => {
getMock.mockRestore();
});
test('testing RECEIVE_DATA async action', async () => {
const dispatch = jest.fn();
await fetchData()(dispatch);
expect(getMock).toHaveBeenCalledWith('foo'); // Success!
expect(dispatch).toHaveBeenCalledWith({
type: 'RECEIVE_DATA',
data: 'mock data'
}); // Success!
});
...or you can mock api.js like this:
import { fetchData } from './sampleAction';
import Api from './api';
jest.mock('./api', () => ({
get: jest.fn(() => Promise.resolve({ data: { data: 'mock data' } }))
}));
test('testing RECEIVE_DATA async action', async () => {
const dispatch = jest.fn();
await fetchData()(dispatch);
expect(Api.get).toHaveBeenCalledWith('foo'); // Success!
expect(dispatch).toHaveBeenCalledWith({
type: 'RECEIVE_DATA',
data: 'mock data'
}); // Success!
});
...or you can auto-mock api.js and fill in the return value for Api.get:
import { fetchData } from './sampleAction';
import Api from './api';
jest.mock('./api'); // <= auto-mock
Api.get.mockResolvedValue({ data: { data: 'mock data' } });
test('testing RECEIVE_DATA async action', async () => {
const dispatch = jest.fn();
await fetchData()(dispatch);
expect(Api.get).toHaveBeenCalledWith('foo'); // Success!
expect(dispatch).toHaveBeenCalledWith({
type: 'RECEIVE_DATA',
data: 'mock data'
}); // Success!
});
...or you can create a manual mock at ./__mocks__/api.js:
export default {
get: jest.fn(() => Promise.resolve({ data: { data: 'mock data' } }))
}
...and activate it in your test like this:
import { fetchData } from './sampleAction';
import Api from './api';
jest.mock('./api'); // <= activate manual mock
test('testing RECEIVE_DATA async action', async () => {
const dispatch = jest.fn();
await fetchData()(dispatch);
expect(Api.get).toHaveBeenCalledWith('foo'); // Success!
expect(dispatch).toHaveBeenCalledWith({
type: 'RECEIVE_DATA',
data: 'mock data'
}); // Success!
});
I am using axios mock adapter to mock HTTP request to test my function. After I defined the behaviour for the function, and then I created an instance of the class to call the function, the result is
**Promise { <pending> }**,
what is the problem? how can I return the value I defined?
Here is my code:
UserService.js
export default class UserService {
getUserInfo = userId => {
const params = {
userId,
};
return axios
.get('https://www.usefortesting.com', {
params: { userId: params },
})
.then(response => response.data.userInfo)
.catch(error => error);
};
}
UserService.test.js
import React from 'react';
import axios from 'axios';
import UserService from './UserService';
import MockAdapter from 'axios-mock-adapter';
describe('testing', () => {
let axiosMock;
const Info = {
userInfo: {
id: '123',
name: 'omg',
},
};
beforeEach(function() {
axiosMock = new MockAdapter(axios);
});
afterEach(() => {
axiosMock.reset();
axiosMock.restore();
});
it('testing', () => {
axiosMock
.onGet('https://www.usefortesting.com', {
params: { userId: 'user_1' },
})
.reply(200, Info);
let userService = new UserService();
let response = userService.getUserInfo('user_1');
console.log(response);
});
});
You need to await for response in your test. Either use callbacks or async/await as shown below.
Your test should be like this:
it('testing', async () => { // notice async here
axiosMock
.onGet('https://www.usefortesting.com', {
params: { userId: 'user_1' },
})
.reply(200, Info);
let userService = new UserService();
let response = await userService.getUserInfo('user_1'); // notice await here
console.log(response);
});
OR
it('testing', () => {
...
userService.getUserInfo('user_1').then(response => {
console.log(response);
});
});
You can check this link on jest docs for more examples.
Also there is error in your getUserInfo() method, in params you are passing an object for userId but you need to pass string or int. What you should do is:
return axios.get('https://www.usefortesting.com', {
params: { userId: params.userId },
})...
OR
return axios.get('https://www.usefortesting.com', {
params,
})...