In this video tutorial, we will learn to create & test a React App using Jest, Mocking using Jest and Spying functions using Jest spyOn command:
A Complete Introduction of Jest was given in our previous tutorial. In this tutorial, we will see how to use Jest for testing React-based apps.
We will learn to create a bootstrapped React app using a simple npm based command and use the same app for writing Jest react tests. We will also explore the concept of snapshot testing and get to know how you can mock & spy React components using the Jest framework and Jest spyon command.
Table of Contents:
React – Getting Started
Jest was built for testing React apps extensively along with the support for all other JavaScript frameworks.
As we will be using the React app to add Jest framework and tests, it’s imperative and indeed a prerequisite to have a basic understanding of React apps.
To get started with a basic React app, please follow the below steps:
#1) In order to create a React app, you can simply use a node package executor (i.e. npx which also comes along with npm) and execute the below command.
npx create-react-app my-app
#2) Once the above command completes, open the project my-app in any editor of your choice – Visual Studio Code that is freely available is a recommended one.
#3) In the terminal/command window (inside editor), run the project using the below command.
npm start
#4) Once the project is compiled, it will open up a new browser tab with the URL http://localhost:3000
#5) Also, please note that all Jest related dependencies come installed as a part of the React project created using the above mentioned npx command.
#6) The project also includes a React testing Library named jest-dom that has a lot of helpful custom DOM element matchers for Jest. (Check here for more details on React concepts)
Jest Snapshot Testing
Snapshot testing is a very useful technique to test React component snapshots using the Jest library.
Let’s first try to understand what snapshot testing essentially means.
Snapshot is nothing but a point of time representation of anything. For example, a screenshot, a camera picture, etc. are all snapshots that represent the details of anything for a point of time.
From the perspective of React, Snapshot is nothing but a point of time representation or output of a React component with the supplied state and behavior.
This is explained with a simple example using the below steps.
#1) To get started with snapshot testing, add the npm package “react-test-renderer” using the below command.
npm i react-test-renderer
#2) Now, let’s create a simple React component that will be our application under test. This component will have a simple state in the form of class variables and page properties.
The component would look as shown below. Let’s name this component as Link (and so the corresponding react component file name will be Link.react.js)
import React from 'react'; export default class Link extends React.Component { constructor() { super(); this.state = { class: "normal", welcomeMessage: "Hi there!" }; } render() { return ( <div> <a className={this.state.class} href={this.props.page || '#'}> {this.props.page} </a> {this.state.welcomeMessage} </div> ); } }
At this time, this is how the file structure will look for the React project.
#3) Let’s add a snapshot test for this component.
a) To get started with the snapshot test – The react-test-renderer Node package is a prerequisite. Install react-node-renderer using the below command.
npm i react-test-renderer
b) Add a new file for adding tests for this new component. Let’s name it as Link.test.js
c) Now add a snapshot test. Here, we will first create a snapshot by rendering the React component.
The test would look as shown below.
import React from 'react'; import Link from './Link.react' import renderer from 'react-test-renderer'; it('renders correctly', () => { const tree = renderer .create(<Link page="www.softwaretestinghelp.com" />) .toJSON(); console.log(tree) expect(tree).toMatchSnapshot(); });
Here in the test, we are creating a JSON representation of the rendered component. We’ve passed the value for “page” property as “www.softwaretestinghelp.com”
d) When the snapshot test is run – a snapshot file of the component is created (with the extension .snap) and saved in the project directory that is reused during the next test executions.
In this case, a snapshot file with the page property as supplied during the test would be used. Let’s see the snapshot file getting generated after running the test using the command “npm test”.
e) A snapshot file gets created under a directory named “__snapshots__” in the project src directory.
Given below is the project structure for this.
The “__snapshots__” directory in the above screenshot gets created in the project root directory when the test executes for the first time.
f) Let’s see how the snapshot file will look like.
Open file – Link.test.js.snap
g) Above shown is the snapshot that gets stored for the given component.
h) Now, for instance, the implementation of the above component changes. For example, let’s change the name of the property page to a site in the component, and let’s try running the test again.
This is how the component is changed (we have changed the property named page to a new property named site).
import React from 'react'; export default class Link extends React.Component { constructor() { super(); this.state = { class: "normal", welcomeMessage: "Hi there!" }; } render() { return ( <div> <a className={this.state.class} href={this.props.site || '#'}> {this.props.site} </a> {this.state.welcomeMessage} </div> ); } }
Now let’s try running the tests again. As we already have the snapshots in the project directory, our test is expected to fail in this scenario – as the Component code has changed and the old snapshot match will be a failure.
Given below is the result that we get while running the test:
(i) Now, let’s assume that these changes are the required changes and require our tests to update the old snapshot. In that case, run the tests with the update command that would overwrite the old snapshot and create a new one based on the new changes in the component itself.
Run Command
yarn test -u
(j) With the above command and updated assertion, you will see the test passing.
Thus, overall Snapshot Testing is a useful technique in order to test the entire component against the final view and store the old result as a snapshot which ensures that no regression issues are introduced as a result of code changes or features or for that matter any refactoring to the existing component.
Video Tutorial: Jest Snapshot Testing
Mocking Using Jest
In this section, we will see how we can use Jest mocks. The mocks can be used in numerous ways as shown below.
For example,
- Mocking the entire React Component
- Mocking single or multiple functions – This is not specific to any Javascript development framework. As Jest is a javascript testing library that is not specific to any particular framework, we can even use Jest to mock a plain old Javascript file containing functions.
- Mocking API calls used inside functions or Javascript code – We can use Jest to mock responses from third-party integration.
Let’s discuss each of these mocking methods in detail.
Mocking React Components
React App is composed of multiple components dependent on each other. For simple understanding, consider React Component as a class – having presentation and logic.
Like any complex system built using Object Oriented Programming is composed of multiple classes, similarly, React App is a collection of components.
Now, when we test a component, we would want to ensure that there are no dependencies that impact testing it i.e. if there are 2 components, on which the component under test is dependent, then if we have the means to mock the dependent components, then we can unit test the component under test in a more complete way.
Let’s try understanding this with the help of the below figure:
Here we have Component1, which is dependent on Component 2 & 3.
While Unit testing Component1, we can replace the Component2 & Component3 using Jest Mocks with their fake or mocked counterparts.
Let’s see how we can set up these mocks. We will use simple Components with an Html text placed inside a div. First, we will see the code for dependent components – Component2 and Component3.
import React, { Component} from 'react' class Component2 extends Component { render() { return( <div id="component2">Hello Component2</div> ) } } export default Component2
import React, { Component} from 'react' class Component3 extends Component { render() { return( <div id="component3">Hello Component3</div> ) } } export default Component3
Now, let’s see how Component1 that has dependent Components will look like. Here you can see, that we are importing the dependent components and using them as a simple HTML tag like <Component2 /> & <Component3 /> respectively.
import React, { Component} from 'react' import Component2 from './component2' import Component3 from './component3' class Component1 extends Component { render() { return( <div> <div id="component1">Hello Component1</div> <Component2 /> <Component3 /> </div> ) } } export default Component1
Now, let’s see how we can write tests for this component. To create a test, create a folder “tests” in the “src” directory. This is just to ensure that our project directory remains clean and organized.
import React, {Component} from 'react' import {render, container} from '@testing-library/react' import Component1 from '../components/component1' // arrange - mock setup jest.mock('../components/component2', () => () => <div id="mockComponent2">Hello Mock Component2</div>) jest.mock('../components/component3', () => () => <div id="mockComponent3">Hello Mock Component3</div>) describe("mock component tests", () => { test("mocked components in react", () => { // act const {container} = render(<Component1 />) // assert console.log(container.outerHTML) const mockComponent2 = container.querySelector('div#mockComponent2') const mockComponent3 = container.querySelector('div#mockComponent3') expect(mockComponent2).toBeInTheDocument() expect(mockComponent3).toBeInTheDocument() }) })
In the above test file, you can see that we have mocked Components1 & 2 using the function jest.mock
jest.mock('../components/component2', () => () => <div id="mockComponent2">Hello Mock Component2</div>)
This setup will simply replace all the invocations of Component2 with this mock representation. So, when we render Component1 in the test, it calls the mocked version of Component2, which we have also asserted by checking whether the Mock div elements exist in the document.
We have used ‘toBeInTheDocument() matcher here. This matcher is React Specific, as React applications final rendered output is nothing but HTML code. Thus, this matcher looks for the given HTML Element to be present in the HTML document created by React.
Video Tutorial: Jest – Mock React Components
Mocking Functions Using Jest
Now, let’s see how we can use Jest mocks, to mock a specific function for a given JavaScript file.
In the above figure, you can see that we are replacing function 2 which is the dependency of function1 with a stubbed/mocked version of function 2
We will first create a test JavaScript file that will serve as an application under test and we will mock some methods there to illustrate the mocking function concept.
function getFullName(firstname, lastname) { return firstname + ' ' + lastname } function greet(firstname, lastname) { return "Hello! " + this.getFullName(firstname,lastname) } module.exports = {getFullName, greet}
We have 2 functions here i.e. greet() and getFullName(). The greet() function uses getFullName() to obtain the full name. We will see how we can replace getFullName() function with its mock implementation while testing the greet() method.
Let’s write a simple test to mock this behavior using the Jest mock function and see how we can validate whether the mocked function was called or not.
test("illustrate mocks", () => { // arrange const mock = jest.fn().mockReturnValue("mocked name") const greeter = require('../app.js') greeter.getFullName = mock // act const result = greeter.greet("aman", "kumar") // assert expect(result).toBe("Hello! mocked name") expect(mock).toHaveBeenCalled() expect(mock).toHaveBeenCalledTimes(1) expect(mock).toHaveBeenCalledWith("aman","kumar") })
Here, we have declared a Jest mock function and set up a return value as “mocked name” which will be returned when the function is called.
const mock = jest.fn().mockReturnValue("mocked name")
Also, to validate that the mock was called, we can use the Jest matchers as shown below.
- toHaveBeenCalled() – Validates if the mock was called.
- toHaveBeenCalledWith(arg1, arg2) – Validates if the mock was called with the given arguments.
- toHaveBeenCalledTimes(n) – Validates the number of times, the Mock would have been called.
There’s another feature of Jest that’s called Spy.
So what are Spies and how do they differ from mocks?
Most of the time, Spies allows the real function call but could be used to validate things like what arguments were used to call the method and also to ascertain whether the method call did happen or not.
Spying in Jest can be done through Jest spyOn command. Jest spyOn takes arguments as the object and the actual function to be spied on i.e. it will actually call the function under test and act as an intermediate interceptor.
test("illustrate spy", () => { // arrange const greeter = require('../app.js') const getFullNameSpy = jest.spyOn(greeter, 'getFullName') // act const result = greeter.greet("aman", "kumar") // assert expect(getFullNameSpy).toHaveBeenCalled() expect(result).toBe("Hello! aman kumar") expect(getFullNameSpy).toHaveBeenCalledWith("aman","kumar") })
So, in the above code, you can observe that:
(i) We’ve set up a spy on method ‘getFullName’ using the below command.
const getFullNameSpy = jest.spyOn(greeter, 'getFullName')
(ii) In assertions, we are verifying that the spy was called with expected arguments.
expect(getFullNameSpy).toHaveBeenCalled() expect(getFullNameSpy).toHaveBeenCalledWith("aman","kumar")
Jest spyOn command can also be used to specify a mock implementation that should be called instead of the actual function using the below command.
const getFullNameSpy = jest.spyOn(greeter, 'getFullName').mockImplementation()
In this case, the real function call is replaced by a mock implementation that’s set up with the spy.
Video Tutorial: Jest- Mock Api Functions
Mocking External API Calls Using Jest
In the below figure, you can see that function1 makes calls to an external API endpoint. For example – calling a payment partner endpoint that gives success or failure response.
Now when we are writing unit tests for this function, we can’t expect to call the external endpoint every time when the tests are run.
There are a couple of reasons for which you would avoid calling external endpoints in the test.
- It might involve cost.
- Its response cannot be controlled. You cannot always test for all the expected response and error codes.
- It might not always be available – if the external endpoint is not available then test results will be flaky.
For all these reasons, it would be very useful if we could control and stub the behavior of the external endpoint and create robust unit tests for our function.
Let’s see how we can achieve mocking API calls using the Jest framework. Axios is an NPM module that could be downloaded/added to the project using the below command.
npm install --save-dev axios
We will be using ‘axios’ module to make API calls in our test function as shown below.
function getUserData() { axios.get('https://reqres.in/api/users/2') .then(response => console.log(response.data)) .catch(error => console.log(error)); }
We are hitting a dummy external endpoint that returns fake data and logging success & error response.
Now, in our unit test, we are going to mock the axios module and return a fake or mocked response when the function calls this external endpoint.
The test code will look as shown below.
const axios = require('axios') jest.mock('axios'); describe("mock api calls", () => { test("mocking external endpoint in axios", () => { // arrange const mockedResponse = {data: {username:'test-user', address:'India'}} axios.get.mockResolvedValue(mockedResponse) const app = require('../app.js') // act app.getUserData() // asserts expect(axios.get).toHaveBeenCalled() expect(axios.get).toHaveBeenCalledWith('https://reqres.in/api/users/2') }) })
Here, it’s important to understand that we are mocking the entire ‘axios’ module here i.e. any call that goes to the Axios module during the test execution will go to the mocked implementation and return a response as configured in the test.
The module is mocked using the below command.
const axios = require('axios') jest.mock('axios');
We have configured the mock using the command below.
axios.get.mockResolvedValue(mockedResponse)
In this way, we can mock responses from external API endpoints. Here we have used a ‘GET’ endpoint, but the same approach could be used for other endpoints like POST, PUT, etc as well.
Video Tutorial: Jest – Mock Api Endpoints
Conclusion
In this tutorial, we learned how to create a simple React App, and saw how Jest React can be used for performing Snapshot tests on React components as well as mocking React Components as a whole.
Also read =>> React Interview questions and answers
We also explored about Mocking using Jest and Spying functions using the Jest spyOn command that calls the real implementation of the method and acts as an interceptor to assert on things like the number of invocations, arguments that the method was called with, etc.