How to Test Axios in Jest

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



Leave a reply



Submit