How do I test axios in Jest?
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
How to test failed request with axios / jest
You should mock axios.get()
with resolved/rejected value. Since you use the try...catch...
statement, the error is thrown by axios.get()
method will be caught, and you didn't rethrow any errors. So the assertion of test case 2 will not match toThrowError
. Instead, you can use jest.spyOn()
to add spy to console.log
, and assert it to be called with the mocked Error
.
Here is an solution only using jest.mock('axios')
to mock axios
module without __mocks__/axios.js
file.
E.g.
getTotalPrice.js
:
const axios = require('axios');
let symbol = process.argv[2];
let quantity = process.argv[3];
const API = `https://rest.coinapi.io/v1/exchangerate`;
const headers = { headers: { 'X-CoinAPI-Key': 'MY TOKEN' } };
const getTotalPrice = async (symbol = 'BTC', quantity = 1) => {
try {
let res = await axios.get(`${API}/${symbol}/USD`, headers);
let data = res.data;
return Math.round(data.rate * quantity * 100) / 100;
} catch (err) {
console.log(err);
}
};
module.exports = { getTotalPrice, API };
getTotalPrice.test.js
:
const axios = require('axios');
const { getTotalPrice, API } = require('./getTotalPrice');
jest.mock('axios');
describe('getTotalPrice', () => {
it('fetches data successfully from an api', async () => {
const res = { data: { rate: 34000 } };
axios.get.mockResolvedValueOnce(res);
await expect(getTotalPrice()).resolves.toEqual(res.data.rate);
});
it('throws an error when incorrect data is passed', async () => {
const logSpy = jest.spyOn(console, 'log');
const err = new Error('Wrong inputs passed in');
axios.get.mockRejectedValueOnce(err);
await getTotalPrice();
expect(logSpy).toBeCalledWith(err);
});
it('uses correct url', async () => {
const res = { data: { rate: 2000 } };
axios.get.mockResolvedValueOnce(res);
await getTotalPrice('ETH');
expect(axios.get).toHaveBeenCalledWith(`${API}/ETH/USD`, { headers: { 'X-CoinAPI-Key': 'MY TOKEN' } });
});
});
test result:
PASS examples/68200193/getTotalPrice.test.js (7.527 s)
getTotalPrice
✓ fetches data successfully from an api (3 ms)
✓ throws an error when incorrect data is passed (14 ms)
✓ uses correct url (1 ms)
console.log
Error: Wrong inputs passed in
at /Users/dulin/workspace/github.com/mrdulin/jest-v26-codelab/examples/68200193/getTotalPrice.test.js:14:17
at Generator.next (<anonymous>)
at /Users/dulin/workspace/github.com/mrdulin/jest-v26-codelab/examples/68200193/getTotalPrice.test.js:8:71
at new Promise (<anonymous>)
at Object.<anonymous>.__awaiter (/Users/dulin/workspace/github.com/mrdulin/jest-v26-codelab/examples/68200193/getTotalPrice.test.js:4:12)
at Object.<anonymous> (/Users/dulin/workspace/github.com/mrdulin/jest-v26-codelab/examples/68200193/getTotalPrice.test.js:12:66)
at Object.asyncJestTest (/Users/dulin/workspace/github.com/mrdulin/jest-v26-codelab/node_modules/jest-jasmine2/build/jasmineAsyncInstall.js:106:37)
at /Users/dulin/workspace/github.com/mrdulin/jest-v26-codelab/node_modules/jest-jasmine2/build/queueRunner.js:45:12
at new Promise (<anonymous>)
at mapper (/Users/dulin/workspace/github.com/mrdulin/jest-v26-codelab/node_modules/jest-jasmine2/build/queueRunner.js:28:19)
at /Users/dulin/workspace/github.com/mrdulin/jest-v26-codelab/node_modules/jest-jasmine2/build/queueRunner.js:75:41
at console.<anonymous> (node_modules/jest-environment-enzyme/node_modules/jest-mock/build/index.js:866:25)
at Generator.throw (<anonymous>)
------------------|---------|----------|---------|---------|-------------------
File | % Stmts | % Branch | % Funcs | % Lines | Uncovered Line #s
------------------|---------|----------|---------|---------|-------------------
All files | 100 | 100 | 100 | 100 |
getTotalPrice.js | 100 | 100 | 100 | 100 |
------------------|---------|----------|---------|---------|-------------------
Test Suites: 1 passed, 1 total
Tests: 3 passed, 3 total
Snapshots: 0 total
Time: 8.034 s
How to mock Axios with Jest?
I would recommend an entirely different way of approaching this. Rather than trying to mock Axios, which is a relatively complicated API that you don't own, test at the network boundary using a tool like msw
. This allows you to freely refactor the implementation without needing to change the tests, giving you more confidence it's still working. You could do things like:
- Factor out repeated config to
axios.create({ baseURL: "http://localhost", ... })
; - Switch to a different library for the requests (e.g.
node-fetch
).
Also if the Axios API changed your tests would start failing, telling you your code no longer works. With a test double, as that would still implement the previous API, you'd have passing but misleading test results.
Here's how that kind of test might look; note that Axios isn't mentioned at all, it's just an implementation detail now and we only care about the behaviour:
import { rest } from "msw";
import { setupServer } from "msw/node";
import client from "./";
const body = { hello: "world" };
const server = setupServer(
rest.get("http://localhost", (_, res, ctx) => {
return res(ctx.status(200), ctx.json(body))
})
);
describe("Client", () => {
beforeAll(() => server.listen());
afterEach(() => server.resetHandlers());
afterAll(() => server.close());
it("should call the API and return a response", async () => {
const response = await client.createRequest("http://localhost/", "GET");
expect(response).toMatchObject({ data: body, status: 200 });
});
});
Note I've had to use .toMatchObject
because you're exposing the whole Axios response object, which contains a lot of properties. This isn't a good API for your client, because now everything using the client is consuming the Axios API; this makes you heavily coupled to it, and dilutes the benefits I mentioned above.
I'm not sure how you're planning to use it, but I'd be inclined to hide the details of the transport layer entirely - things like status codes, headers etc. are not likely relevant to the business logic in the consumer. Right now you really just have:
const createRequest = (url, method) => axios({ method, url });
at which point your consumers might as well just be using Axios directly.
How to test axios api using jest and react testing library?
You must mock axios and look if axios methods are called with the right parameters for example.
apis.spec.js
import apis from './apis';
jest.mock('axios'); // This overwrites axios methods with jest Mock
import axios from 'axios';
describe('Test Apis', () => {
describe('getResource', () => {
describe('with success', () => {
const url = 'http://test-url.com';
const onComplete = jest.fn();
const data = {};
beforeEach(() => {
axios.get.mockResolvedValue(data);
});
it('should call axios get with given url', () => {
getResource(url, onComplete);
expect(axios.get).toBeCalledWith(url);
});
it('should call onComplete callback with response', async () => { // do not forget 'async'
await getResource(url, onComplete); // notice the 'await' because onComplete callback is called in '.then'
expect(onComplete).toBeCalledWith(data);
});
});
});
});
You could do the same with the error response and POST method. You could also test your LookingGlassAPIs
by mocking your apis.js
methods, or again by mocking axios
, it depends of your "unit" definition inside your project.
How to mock Axios get response with jest
I think replacing this:
axios.get.mockResolvedValue(mockResp);
With this:
axios.get = jest.fn(() => mockResp);
Should help you.
P.S.: this call expect(axios.get)
won't wait for promise to resolve.
Testing axios.create() instance with jest
As you can see, return axiosInstance({
means axiosInstance
is a function, then if you want to test axiosInstanceCounter
function, just mock axiosInstance
as a normal function(in your case the api call will not return anything):
api.test.ts // testing for api.ts
import { AxiosPromise } from "axios";
import { mocked } from "ts-jest/utils"; // a helper function from ts-jest
import { axiosInstanceCounter } from '../features/api/axiosTest'; // should be ../features/api ???
import { axiosInstance } from '../features/instancec/axios-instance';
jest.mock("../features/instancec/axios-instance");
describe("api", () => {
describe("axiosInstanceCounter()", () => {
it("should call api with correct parameters", async () => {
// mock to resolve a Promise<void>
mocked(axiosInstance).mockResolvedValue(Promise.resolve() as unknown as AxiosPromise<void>);
await axiosInstanceCounter();
expect(axiosInstance).toHaveBeenCalledWith({ method: "post" });
});
});
});
Unit Testing with Jest and React - Axios Get Inside useEffect
Please try this example.
import React from "react";
import { mount, shallow } from "enzyme";
import axios from "axios";
import { act } from "react-dom/test-utils";
import App from "./App";
jest.mock("axios");
// mock data
const url= "YOUR_URL",
describe("App test", () => {
let wrapper;
// clear all mocks
afterEach(() => {
jest.clearAllMocks();
});
test("load app", async () => {
// mock axios promise
await act(async () => {
await axios.get.mockImplementationOnce(() => Promise.resolve(url));
wrapper = mount(<App />);
});
wrapper.update();
await expect(axios.get).toHaveBeenCalledTimes(1);
});
});
Related Topics
Capture Browser Console Logs with Capybara
Uncaught Referenceerror: Reactdom Is Not Defined
Creating a "Sticky" Fixed-Position Item That Works on iOS Safari
Dt: Link Binding Is Lost After Re-Rendering the Table
How to Execute Array of Promises in Sequential Order
Ios: Authentication Using Xmlhttprequest - Handling 401 Response
IE8 Var W= Window.Open() - "Message: Invalid Argument."
Converting JSON Format to CSV to Upload Data Table in R to Produce D3 Bubble Chart
How to Remove a Character from a String Using JavaScript
Using Enter Key with Action Button in R Shiny
How to Validate a Radio Button
Passing Array of Objects from Js to Rails
What Is the "Best" Way to Get and Set a Single Cookie Value Using JavaScript
How to Run Node Js Code from Npm Inside of Swift
Has Anyone Else Run into Problems Styling Twitter Typeahead's Searchbar