Jest Mock: mock external file/lib method in test cases

Introduction

In your react project, you may have used some external libraries or file method for developing some  functionalities. So when you try to test this particular functionality that you have developed, you get stuck trying to understand how to test this external method(s) (from npm libs) which are are being imported and used. I know this because, I was stuck in similar situation and the official jest docs had a lot of things in it but it was not specific enough to make me understand and it took me some time to wrap my head around this concept. This particular article is my solution to try and mock a particular external method (using jest mock method) in the test case for a simple example app. Hope this article saves time for a lot of fellow devs in the community!

Prerequisites

Before we start, I am assuming you know how to setup a react project, develop your functional component using react js and write basic test cases in react using JEST and enzyme. If not, there is definitely a ton of youtube videos, udemy courses, articles etc. on the internet to get familiar with it.

This article is focusing just on the one hurdle i.e HOW TO USE JEST.MOCK?!

The application

For the sake of this article, I have used typescript and my react project and for writing test cases I am using JEST and enzyme. The application that I have developed is a simple app that calculates age when you input your date of birth.

Application output

 so the main logic is written in app.jsx  which is as follows:

import React, { useState } from "react";
import "./styles.css";
import moment from "moment";
export default function App() {
const [date, setDate] = useState("");
const [age, setAge] = useState(0);
const calculateAge = () => {
console.log('diff called');
const calcAge = moment().diff(date, "years");
setAge(calcAge);
};
return (
<div className="App">
<h2>How old are you?</h2>
<label>Birthday: </label>
<input
type="date"
onChange={(e) => setDate(e.target.value)}
value={date}
id="birthday"
name="birthday"
/>
<button disabled={!date.length} onClick={calculateAge}>
Calculate Age
</button>
<div>Your age is: {age}</div>
</div>
);
}
view raw App.tsx hosted with ❤ by GitHub

Test cases

once we have the app file ready, we can start writing the test cases. I have written the following test cases:

  • The application should render as expected 
  • the button should be disabled when the date is not picked
  •  once the date is picked the button should not be disabled
  •  moment library function is called it should be called with the given mock params 

The jest test cases for the for above mentioned cases should look as follows:

import { shallow, configure } from "enzyme";
import React from "react";
import App from "../App";
import Adapter from 'enzyme-adapter-react-16';
//define mock for moment lib
const diff = jest.fn();
const momentObj = () => ({
diff
})
jest.mock('moment', () => momentObj)
configure({ adapter: new Adapter() });
describe("App", () => {
let wrapper;
beforeEach(() => {
wrapper = shallow(<App />);
});
it("should render properly", () => {
expect(wrapper).toMatchSnapshot();
});
it('should have calculate button disabled, if date is not selected', () => {
const calcButton = wrapper.find('button').at(0);
expect(calcButton.prop('disabled')).toBe(true);
});
it('should have calculate button enabled after date is selected', () => {
const picker = wrapper.find('input').at(0);
picker.prop('onChange')({
target: {
value: '1990-10-02'
}
});
const calcButton = wrapper.find('button').at(0);
expect(calcButton.prop('disabled')).toBe(false);
});
it('should execute moment method to calculate age on button click', () => {
const moment = require('moment');
const picker = wrapper.find('input').at(0);
picker.prop('onChange')({
target: {
value: '1990-10-02'
}
});
const calcButton = wrapper.find('button').at(0);
calcButton.prop('onClick')();
expect(diff).toHaveBeenCalled();
})
});
view raw App.spec.tsx hosted with ❤ by GitHub

Once we have the app spec/test file ready, we can just run the test command and run the test cases. It should show the following result:

test run results

What is happening

Let’s break down what’s happening in these test cases that we have defined.  So the first 2-3 test cases are basic normal test cases nothing fancy we are just checking if the application is working as expected, the button should be disabled if the date is not picked and should not be disabled otherwise.

 The final test case, however, we need to check whether a particular library’s i.e moment JS (which we know component uses internally) function/method has been called or not and if it is called is it called with the expected params.  this particular case is crucial for the overall coverage of the application code that we have developed!

overall coverage for app.tsx

Before we start our mock, now we need to analyse and understand what moment JS class/method/function we are importing. We will focus the method we care about (our test case’s context) and we will mock that in our test case. 

In our case, we have imported a function called moment which returns an object that contains the key value pair (diff) we are looking for.

The mock for moment.diff method

Userful Links

Conclusion

 once you understand how the moment library‚Äôs (or any libraries or files for that matter) object and the function return works, you can easily mock it and create our mock object for your test case. 

Jest.Mock can be used similarly in various scenario where you don’t have access to the the method/function/class which is used by the file you are testing/ writing test cases for!

Hope this article has generated some value and solved your doubts related to mocking methods using Jest.mock. If not, you can comment your doubts below and we can have a conversation on it.

1
1