How To Mock Dependencies With Jest

Simulate external dependencies effortlessly with Jest

When we want to test our code, some things have dependencies inside, and you don't want to call this stuff. You won't be sure your code works, not external dependencies or external code unrelated to my code.

Today we will add tests to our example weather app using Jest and Mock the dependencies.

👉🏽 Aqui puedes leer otros articulos sobre Frontend y Angular en español

The app

Our example app has two principal codes, the weatherAPI.js and showWeather.js; the showWeather uses weatherAPi.js code to display the data.

The weatherAPI.js

const getWeather = (format) => {
    const min = format = 'C' ? 50 : 100;
    return  50;
}

module.exports = { getWeather}

The showWeather.js

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

const messageWeather = () => {
    let weather = weatherAPI.getWeather('C');
    return `Today weather is ${weather}, have a nice day!`
}

module.exports = { messageWeather }

We have a clear idea about our app, and the next step is to add tests for showWeather code.

You see the final example code from GitHub repo github.com/danywalls/02-mocking-code

Writing the test

We use jest functions test to declare our test and the assertion functions expect and toBe matchers.

The idea focuses on mocking; to read more about the assertion functions, feel free to read the official documentation. (jestjs.io/docs/expect)[https://jestjs.io/do...

const weatherAPI = require('./weatherAPI');
const { messageWeather } = require('./showWeather');

test('should return weather message with celsius temperature', () => {
    const result = messageWeather();
    const expected = `Today weather is 50, have a nice day!`;
    expect(result).toBe(expected);
})

We can run our test npx jest. And it works. Perfect!

 PASS  ./showWeather.test.js
  ✓ Return the Weather with C  (2 ms)

Test Suites: 1 passed, 1 total
Tests:       1 passed, 1 total
Snapshots:   0 total
Time:        0.376 s, estimated 1 s

Nice, but our test calls the getWeather using the actual code, and my test only needs to cover the showWeather code.

How to fake the weatherAPI methods?

Jest provides a few ways to mock the weatherAPI methods.

We will use the three options with the same result, but you can the best for you.

Override functions with jest.fn

The easiest way is to reassign the getWeather method and assign a jest.fn mock function, we update the test with the following points.

  1. assign jest.fn and return 20 by default.

  2. validate the getWeather method to get the C parameter.

  3. validate the result and expect are equal.

test('should return weather message with celsius temperature', () => {
    weatherAPI.getWeather = jest.fn((format) => `20`);

    expect(weatherAPI.getWeather).toHaveBeenCalledWith('C');
    const result = messageWeather();
    const expected = `Today weather is 20, have a nice day!`;

    expect(weatherAPI.getWeather).toHaveBeenCalledWith('C');
    expect(result).toBe(expected);
    weatherAPI.getWeather.mockRestore();
})

The function mockRestore, to assign the original value to getWeather function.

Use jest.spyOn

The spyOn help us to assign a mock function to the object, in our case, the weatherAPI object.

The spyOn override and the function getWeather mock have the mock implementation function to return the simulated value.

    jest.spyOn(weatherAPI, 'getWeather')
    weatherAPI.getWeather.mockImplementation((format) => `20`)

    const result = messageWeather();
    const expected = `Today weather is 20, have a nice day!`;

    expect(weatherAPI.getWeather).toHaveBeenCalledWith('C');
    expect(result).toBe(expected);

    weatherAPI.getWeather.mockRestore();

Mock the module

Instead of mocking every function, jest helps us mimic the entire module using jest.mock.

Create mocks directory into the same path of the file to mock, export the functions, and create the module's name in our case weatherAPI.

module.exports = {
    getWeather: jest.fn((format) => `20`)
}

In our test, the to jest uses the mock module with jest.mock.

jest.mock('./weatherAPI');
test('should return weather message with celsius temperature', () => {

    const result = messageWeather();
    const expected = `Today weather is 20, have a nice day!`;
    expect(weatherAPI.getWeather).toHaveBeenCalledWith('C');
    expect(result).toBe(expected);
    weatherAPI.getWeather.mockRestore();

})

Testing async functions

Async functions are widespread in our code. Let's add a new function promise into the weatherAPI and use it in the showWeather.js.

Read more about async and await keyword

const getMetaWeather = async () => {
    return new Promise((resolve) => {
        resolve('Summer time!')
    })

}

module.exports = { getWeather, getMetaWeather}

The getMetaWeather function is a promise; to use it in our new function, showWeatherStatus, we use the await and async to wait for the getMetaWeather response.

const showWeatherStatus = async () => {
    let weather =  await weatherAPI.getMetaWeather();
    return `${weather}, Enjoy!`
}

module.exports = { messageWeather, showWeatherStatus }

The next step is to update our test to cover the showWeatherStatus, editing the mocks/weatherAPI.js to return the mock version of the getMetaWeather function returns a promise with the mock data.

getMetaWeather: jest.fn(() => new Promise((resolve) => resolve('Great day') ))

We create a new test for async weather status, but using the async and await keyword because we update the mocks, our tests automatic will get the mocking example :)

test('Should return async weather status', async () => {
    const result = await showWeatherStatus();
    const expected = 'Great day, Enjoy!';
    expect(result).toBe(expected);
})

In all cases, our test will work.

 PASS  ./showWeather.test.js
  Show Weather functions
    ✓ should return weather message with celsius temperature (3 ms)
    ✓ Should return async weather (1 ms)

Test Suites: 1 passed, 1 total
Tests:       2 passed, 2 total
Snapshots:   0 total
Time:        0.383 s, estimated 1 s

Final

Jest makes it easy to test our code and external dependencies. I recommend using the mocks overriding the complete module because it makes it easy to update the mocks and read the tests because it only has assertions methods.

If you want to read more about mocking with jest, please read the official documentation.