Sandra Schlichting
Sandra Schlichting

Reputation: 25996

How to mock Axios inside a custom function?

Question

How can I mock axios.get() when it is inside my own custom function?

I am completely lost at this point. Hope someone can see what I am doing wrong.

Details

I have the below getString() function which downloads the html from a website and returns a value > 0 if the string was found and -1 if string not found.

Since getString() uses axios.get() to download the html, I would like to Jest mock this call.

This article is the closest I can find to my situation, but in his case, he mocks a standalone axios.request(), where mine axios.get() is inside my custom getString().

My attempt is this:

getString.test.js

const axios = require('axios');
const getString = require('./getString');

jest.mock('./getString', () => {
  return {
    baseURL: 'localhost',
    get: jest.fn().mockResolvedValue({
      data: 'xxx If you are the website administrator xxx'
    }),
  }
});

packages.json

{
  "name": "jest",
  "version": "1.0.0",
  "description": "",
  "main": "getString.js",
  "scripts": {
    "test": "jest"
  },
  "keywords": [],
  "author": "",
  "license": "ISC"
}

I have done npm init -y && npm install --save-dev jest, but npm run test gives me

$ npm run test

> [email protected] test /home/mje/projects/jest
> jest

sh: jest: command not found
npm ERR! code ELIFECYCLE
npm ERR! syscall spawn
npm ERR! file sh
npm ERR! errno ENOENT
npm ERR! [email protected] test: `jest`
npm ERR! spawn ENOENT
npm ERR! 
npm ERR! Failed at the [email protected] test script.
npm ERR! This is probably not a problem with npm. There is likely additional logging output above.
npm WARN Local package.json exists, but node_modules missing, did you mean to install?

The simple PoC works from the docs.

index.js

const getString = require('./getString');

(async function(){
  'use strict'

  const isOk = await getString({
    url:          'http://localhost',
    string:       'If you are the website administrator',
    timeout:      1000,
    maxRedirects: 0
  });

  console.log(isOk);
})();

getString.js

const axios = require('axios');
const qs = require('qs');

module.exports = async (options) => {
  options              = options || {};
  options.url          = options.url || {};
  options.string       = options.string || null;
  options.timeout      = options.timeout || 1000;
  options.maxRedirects = options.maxRedirects || 0;

  try {
    const response = await axios.get(options.url, {
      timeout: options.timeout,
      maxRedirects: options.maxRedirects,
      validateStatus: null,
      transformResponse: [function (data) {
        return data.search(options.string);
  }]
    });
    return await response.data;
  } catch (error) {
    return -1;
  }
};

Upvotes: 1

Views: 1876

Answers (1)

Teneff
Teneff

Reputation: 32158

First verify if jest is within your dependencies, and if it's not - install it

yarn add --dev jest || npm i -D jest

and then test your implementation by mocking axios rather than the implementation that you want to test.

And the rest of the getString could look something like this

const axios = require('axios');
const getString = require('./getString');

jest.mock('axios')

const mockResponseData = {
  hello: "world"
}

describe('getString', () => {
  describe('axios returning resolved promise', () => {
    beforeAll(() => {
      // returning transformed mock
      axios.get.mockResolvedValue({
        data: mockResponseData
      })
    })

    describe('called with arguments', () => {
      let result
      beforeAll(() => {
        result = getString({
          url: 'http://localhost',
          string: 'If you are the website administrator',
          timeout: 1000,
          maxRedirects: 0
        })
      })

      it('should call axios.get', async () => {
        await result
        expect(axios.get).toHaveBeenCalledWith(
          "http://localhost",
          {
            "maxRedirects": 0,
            "timeout": 1000,
            "transformResponse": [
              expect.any(Function)
            ],
            "validateStatus": null
          }
        )
      })

      it('should return the response.data', async () => {
        expect(await result).toEqual(mockResponseData)
      })
    })
  })

  describe('axios returning rejected promise', () => {
    beforeAll(() => {
      // returning transformed mock
      axios.get.mockRejectedValue({
        data: mockResponseData
      })
    })

    describe('called with arguments', () => {
      let result
      beforeAll(() => {
        result = getString({
          url: 'http://localhost',
          string: 'If you are the website administrator',
          timeout: 1000,
          maxRedirects: 0
        })
      })

      it('should return -1', async () => {
        expect(await result).toEqual(-1)
      })
    })
  })
})

Edit: in order to achieve 100% coverage you'll also have to test your implementation's transformResponse and in order to do that you can mock the implementation like this

// this is not tested
import mockResponseBody from './localMock.json';

axios.get.mockImplementation((url, { transformResponse }) =>
    Promise.resolve({
        data: transformResponse.reduce((acc, fn) => fn(acc), mockResponseData)
    })
)


working example

Upvotes: 2

Related Questions