I am trying to test a function with Axios get request. when I get a response I'm calling another function to filter the JSON document.
// sample.js
import axios from "axios";
public async getFruitData{
const response = await axios.get("https://api.url.com/search/fruit", {
params: {
client_id: process.env.REACT_APP_UNSPLASH_TOKEN,
query: term
}
});
return this.filtersResponse(response.data.results); //<---throwing error
};
filterResponse(detailsResponse) {
const excludeFruit = ['apple'];
return detailsResponse.filter(record => !excludeFruit.includes(record.fruit_type));
}
here my jest test
import axios from 'axios';
import Fruit from '../app/fruit';
jest.mock('axios');
test('it filter jsont fruit ', async () => {
mockedAxios.get.mockImplementationOnce(() => Promise.resolve({ data: 'test' }));
const result = await getFruitData();
expect(result).toBe({"data": "test"});
expect(filterResponse(record)).toBeDefined()
});
when I run the test it gave me the following error --> TypeError: Cannot read property 'status' of undefined
How can I test this scenario
I have a file which I am trying to unit test. I wish to test that the API object is created by calling the feathers and auth library methods as well as testing that the exported login and logout methods call the appropriate library methods.
I would like to mock all calls to the library methods so as to just unit test this file.
This is what I have so far:
api.js
import feathers from '#feathersjs/feathers'
import auth from '#feathersjs/authentication-client'
export const DEFAULT_TIMEOUT = 30
const api = feathers()
api.configure(auth({ storage: window.localStorage, timeout: DEFAULT_TIMEOUT * 1000 }))
const login = async (credentials) => {
return api.authenticate(credentials)
}
const logout = async () => {
return api.logout()
}
export default { login, logout }
api.test.js
import feathers from '#feathersjs/feathers'
import auth from '#feathersjs/authentication-client'
import api, { DEFAULT_TIMEOUT } from './api'
const mockFeathers = {
configure: jest.fn(),
authenticate: jest.fn(),
logout: jest.fn()
}
jest.mock('#feathersjs/feathers', () => jest.fn(() => mockFeathers))
jest.mock('#feathersjs/authentication-client', () => jest.fn(() => 'AUTH'))
describe('helpers/api', () => {
it('creates a Feathers app with authentication', () => {
expect(feathers).toHaveBeenCalled()
expect(auth).toHaveBeenCalledWith({
storage: window.localStorage,
timeout: DEFAULT_TIMEOUT * 1000
})
expect(mockFeathers.configure).toHaveBeenCalledWith('AUTH')
})
describe('login', () => {
it('authenticates with the Feathers app', async () => {
const loginResult = { loggedIn: true }
mockFeathers.authenticate.mockImplementationOnce(() => loginResult)
const credentials = { email: 'user#example.com', password: 'password' }
const result = await api.login(credentials)
expect(mockFeathers.authenticate).toHaveBeenCalledWith(credentials)
expect(result).toEqual(loginResult)
})
})
describe('logout', () => {
it('logs out from the Feathers app', async () => {
await api.logout()
expect(mockFeathers.logout).toHaveBeenCalled()
})
})
})
This fails with ReferenceError: mockFeathers is not defined because it seems that api.js is being loaded (and therefore the feathers method called) before mockFeathers is defined. I have tried moving the import api line below the definition of mockFeathers but that doesn't make a difference.
How can I mock the result of the feathers() call before the api.js file is loaded?
I did manage to achieve this by removing the import entirely and requireing the api.js in a beforeAll block. This works but seems a little messy to me, and wouldn't scale well if you had multiple individual imports from the required file.
api.test.js
// Remove the import line.
//import api, { DEFAULT_TIMEOUT } from './api'
describe('helpers/api', () => {
let api, DEFAULT_TIMEOUT
beforeAll(() => {
// Require the file here so that the mock has time to be defined.
;({ default: api, DEFAULT_TIMEOUT } = require('./api'))
})
// ...
})
New to node.js. I am writing a JS API client that wraps the underlying axios library. In the unit tests I am mocking axios using Jest.
In the constructor of my API class I pass in a URL, and use the axios.create function to create a custom instance of axios and bind it to the the client property.
The problem arises when I mock the axios dependency with jest.mock('axios') - A TypeError is being thrown in the test when an attempt is made to call axios.get:
TypeError: Cannot read property `get` of undefined
I understand why this is happening, but I've not found a way to enable me to mock axios and not have the client field be undefined. Is there a way to get around this, other than injecting axios through the constructor?
Client code and test below:
client.js
jest.mock("axios");
const axios = require("axios");
const mockdata = require("./mockdata");
const ApiClient = require("../../../src/clients/apiclient");
const BASE_URL = "https://www.mock.url.com"
const mockAxiosGetWith = mockResponse => {
axios.get.mockResolvedValue(mockResponse);
};
test("should make one get request", async () => {
mockAxiosGetWith(MOCK_RESPONSE)
// the client field in apiclient is undefined
// due to the jest module mocking of axios
const apiclient = new ApiClient.AsyncClient(BASE_URL);
// TypeError: Cannot read property `get` of undefined
return await apiclient.get("something").then(response => {
expect(axios.get).toHaveBeenCalledTimes(1);
});
});
client.test.js
const axios = require("axios");
const getClient = (baseUrl = null) => {
const options = {
baseURL: baseUrl
};
const client = axios.create(options);
return client;
};
module.exports = {
AsyncClient: class ApiClient {
constructor(baseUrl = null) {
this.client = getClient(baseUrl);
}
get(url, conf = {}) {
return this.client
.get(url, conf)
.then(response => Promise.resolve(response))
.catch(error => Promise.reject(error));
}
}
};
You need to mock axios so it will return an object which holds the create function which should return the object with the get
import axios from 'axios'
jest.mock('axios', () => ({create: jest.fn()}))
test("should make one get request", async () => {
const get = jest.fn(()=>Promise.resolve(MOCK_RESPONSE))
axios.create.mockImplementation(()=>({get}))
const apiclient = new ApiClient.AsyncClient(BASE_URL);
await apiclient.get("something")
expect(get).toHaveBeenCalledTimes(1);
});
I have a node module which exports a few classes, one of which is Client, which I use to create a client (having a few APIs as methods).
I'm trying to test my module which uses this node module as a dependency using Jest. However, I've been unable to successfully mock the one method (say search()) in the Client class.
Here is my spec for myModule:
//index.spec.ts
import * as nock from 'nock';
import * as externalModule from 'node-module-name';
import { createClient } from './../../src/myModule';
describe(() => {
beforeAll(() => {
nock.disableNetConnect();
});
it('test search method in my module', () => {
jest.mock('node-module-name');
const mockedClient = <jest.Mock<externalModule.Client>>externalModule.Client;
const myClient = createClient({/*params*/}); //returns instance of Client class present in node module by executing Client() constructor
myClient.searchByName('abc'); //calls search API - I need to track calls to this API
expect(mockedClient).toHaveBeenCalled();
expect(mockedClient.prototype.search).toHaveBeenCalledWith('abc');
});
});
This, however, doesn't create a mock at all and triggers a nock error since the search API tries to connect to the url (given through params).
I've also tried mocking the Client class like the following. While successfully creating a mock for the Client class and also the search API (verified that search() is also mocked through console logs), it gives me an error while I try to check if search() has been called.
externalModule.Client = jest.fn(() => { return { search: jest.fn(() => Promise.resolve('some response')) } });
//creates the mock successfully, but not sure how to track calls to 'search' property
const client = myModule.createClient(/*params*/);
client.searchByName('abc');
expect(externalModule.Client).toHaveBeenCalled(); //Successful
expect(externalModule.Client.prototype.search).toHaveBeenCalled(); //returns error saying "jest.fn() value must be a mock function or spy, Received: undefined"
I'm not sure what I'm doing wrong. Thank you in advance.
Mocking whole module
Try moving jest.mock to the top of file
//index.spec.ts
const search = jest.fn();
jest.mock('node-module-name', () => ({
Client: jest.fn(() => ({ search }))
}));
import * as nock from 'nock';
import * as externalModule from 'node-module-name';
import { createClient } from './../../src/myModule';
describe(() => {
beforeAll(() => {
nock.disableNetConnect();
});
it('test search method in my module', () => {
const myClient = createClient({/*params*/});
myClient.searchByName('abc');
expect(externalModule.Client).toHaveBeenCalled();
expect(search).toHaveBeenCalledWith('abc');
externalModule.Client.mockClear();
search.mockClear();
});
});
Mocking only Client
Create search constant and track it.
const search = jest.fn();
externalModule.Client = jest.fn(() => ({ search }));
const client = myModule.createClient(/*params*/);
client.searchByName('abc');
expect(externalModule.Client).toHaveBeenCalled();
expect(search).toHaveBeenCalled();
Here is how I mocked it. I had to change naming and removing some code to avoid exposing original source.
jest.mock('../foo-client', () => {
return { FooClient: () => ({ post: mockPost }) }
})
Full code.
// foo-client.ts
export class FooClient {
constructor(private config: any)
post() {}
}
// foo-service.ts
import { FooClient } from './foo-client'
export class FooLabelService {
private client: FooClient
constructor() {
this.client = new FooClient()
}
createPost() {
return this.client.post()
}
}
// foo-service-test.ts
import { FooService } from '../foo-service'
const mockPost = jest.fn()
jest.mock('../foo-client', () => {
return { FooClient: () => ({ post: mockPost }) }
})
describe('FooService', () => {
let fooService: FooService
beforeEach(() => {
jest.resetAllMocks()
fooService = new FooService()
})
it('something should happened', () => {
mockPost.mockResolvedValue()
fooService.createPost()
})
})
I have this action in React:
export function fetchPosts() {
const request = axios.get(`${WORDPRESS_URL}`);
return {
type: FETCH_POSTS,
payload: request
}
}
How do I test Axios in this case?
Jest has this use case on their site for asynchronous code where they use a mock function, but can I do this with Axios?
Reference: An Async Example
I have done this so far to test that it is returning the correct type:
it('should dispatch actions with the correct type', () => {
store.dispatch(fetchPosts());
let action = store.getActions();
expect(action[0].type).toBe(FETCH_POSTS);
});
How can I pass in mock data and test that it returns?
Without using any other libraries:
import * as axios from "axios";
// Mock out all top level functions, such as get, put, delete and post:
jest.mock("axios");
// ...
test("good response", () => {
axios.get.mockImplementation(() => Promise.resolve({ data: {...} }));
// ...
});
test("bad response", () => {
axios.get.mockImplementation(() => Promise.reject({ ... }));
// ...
});
It is possible to specify the response code:
axios.get.mockImplementation(() => Promise.resolve({ status: 200, data: {...} }));
It is possible to change the mock based on the parameters:
axios.get.mockImplementation((url) => {
if (url === 'www.example.com') {
return Promise.resolve({ data: {...} });
} else {
//...
}
});
Jest v23 introduced some syntactic sugar for mocking Promises:
axios.get.mockImplementation(() => Promise.resolve({ data: {...} }));
It can be simplified to
axios.get.mockResolvedValue({ data: {...} });
There is also an equivalent for rejected promises: mockRejectedValue.
Further Reading:
Jest mocking documentation
A GitHub discussion that explains about the scope of the jest.mock("axios") line.
A related question which addresses applying the techniques above to Axios request interceptors.
Using jest functions like mockImplementation in TypeScript: Typescript and Jest: Avoiding type errors on mocked functions
I used axios-mock-adapter.
In this case the service is described in ./chatbot.
In the mock adapter you specify what to return when the API endpoint is consumed.
import axios from 'axios';
import MockAdapter from 'axios-mock-adapter';
import chatbot from './chatbot';
describe('Chatbot', () => {
it('returns data when sendMessage is called', done => {
var mock = new MockAdapter(axios);
const data = { response: true };
mock.onGet('https://us-central1-hutoma-backend.cloudfunctions.net/chat').reply(200, data);
chatbot.sendMessage(0, 'any').then(response => {
expect(response).toEqual(data);
done();
});
});
});
You can see it the whole example here:
Service:
https://github.com/lnolazco/hutoma-test/blob/master/src/services/chatbot.js
Test:
https://github.com/lnolazco/hutoma-test/blob/master/src/services/chatbot.test.js
I could do that following the steps:
Create a folder __mocks__/ (as pointed by #Januartha comment)
Implement an axios.js mock file
Use my implemented module on test
The mock will happen automatically
Example of the mock module:
module.exports = {
get: jest.fn((url) => {
if (url === '/something') {
return Promise.resolve({
data: 'data'
});
}
}),
post: jest.fn((url) => {
if (url === '/something') {
return Promise.resolve({
data: 'data'
});
}
if (url === '/something2') {
return Promise.resolve({
data: 'data2'
});
}
}),
create: jest.fn(function () {
return this;
})
};
Look at this
The function to test album.js
const fetchAlbum = function () {
return axios
.get("https://jsonplaceholder.typicode.com/albums/2")
.then((response) => {
return response.data;
});
};
The test album.test.js
const axios = require("axios");
const { fetchAlbum } = require("../utils.js");
jest.mock("axios");
test("mock axios get function", async () => {
expect.assertions(1);
const album = {
userId: 1,
id: 2,
title: "sunt qui excepturi placeat culpa",
};
const payload = { data: album };
// Now mock axios get method
axios.get = jest.fn().mockResolvedValue(payload);
await expect(fetchAlbum()).resolves.toEqual(album);
});
I've done this with nock, like so:
import nock from 'nock'
import axios from 'axios'
import httpAdapter from 'axios/lib/adapters/http'
axios.defaults.adapter = httpAdapter
describe('foo', () => {
it('bar', () => {
nock('https://example.com:443')
.get('/example')
.reply(200, 'some payload')
// test...
})
})
For those looking to use axios-mock-adapter in place of the mockfetch example in the Redux documentation for async testing, I successfully used the following:
File actions.test.js:
describe('SignInUser', () => {
var history = {
push: function(str) {
expect(str).toEqual('/feed');
}
}
it('Dispatches authorization', () => {
let mock = new MockAdapter(axios);
mock.onPost(`${ROOT_URL}/auth/signin`, {
email: 'test#test.com',
password: 'test'
}).reply(200, {token: 'testToken' });
const expectedActions = [ { type: types.AUTH_USER } ];
const store = mockStore({ auth: [] });
return store.dispatch(actions.signInUser({
email: 'test#test.com',
password: 'test',
}, history)).then(() => {
expect(store.getActions()).toEqual(expectedActions);
});
});
In order to test a successful case for signInUser in file actions/index.js:
export const signInUser = ({ email, password }, history) => async dispatch => {
const res = await axios.post(`${ROOT_URL}/auth/signin`, { email, password })
.catch(({ response: { data } }) => {
...
});
if (res) {
dispatch({ type: AUTH_USER }); // Test verified this
localStorage.setItem('token', res.data.token); // Test mocked this
history.push('/feed'); // Test mocked this
}
}
Given that this is being done with jest, the localstorage call had to be mocked. This was in file src/setupTests.js:
const localStorageMock = {
removeItem: jest.fn(),
getItem: jest.fn(),
setItem: jest.fn(),
clear: jest.fn()
};
global.localStorage = localStorageMock;
New tools for testing have been introduced since the question was initially answered.
The problem with mocking is that you often test the mock and not the real context of your code, leaving some areas of this context untested.
An improvement over telling axios what promise to return is intercepting http requests via Service Workers.
Service worker is a client-side programmable proxy between your web app and the outside world. So instead of mocking promise resolution it is a more broader solution to mock the proxy server itself, intercepting requests to be tested. Since the interception happens on the network level, your application knows nothing about the mocking.
You can use msw (Mock Service Worker) library to do just that. Here is a short video explaining how it works.
The most basic setup I can think of is this:
1️⃣ set up handlers, which are similar to express.js routing methods;
2️⃣ set up mock server and pass handlers as it’s arguments;
3️⃣ configure tests to so that mock server will intercept our requests;
4️⃣ perform tests;
5️⃣ close mock server.
Say you want to test the following feature:
import axios from "axios";
export const fetchPosts = async () => {
const request = await axios.get("/some/endpoint/");
return {
payload: request,
};
};
Then test could look like this:
import { rest } from "msw";
import { setupServer } from "msw/node";
import fetchPosts from "./somewhere";
// handlers are usually saved in separate file(s) in one destined place of the app,
// so that you don't have to search for them when the endpoints have changed
const handlers = [ 1️⃣
rest.get("/some/endpoint/", (req, res, ctx) =>
res(ctx.json({ message: "success" }))
),
];
const server = setupServer(...handlers); 2️⃣
beforeAll(() => {
server.listen(); 3️⃣
});
describe("fetchPosts", () => {
it("should return 'success' message", async () => {
const resp = await fetchPosts();
expect(resp.payload?.data?.message).toEqual("success"); 4️⃣
});
});
afterAll(() => {
server.close(); 5️⃣
});
The configuration may be different depending on framework you are using. Some general examples for, among others, React (both REST and GraphQL) and Angular can be found on MSW’ repo. A Vue example is provided by VueMastery.
You can also find examples on MSW' recipes page.