Test your React App with react testing library and jest-dom

Aatif Bandey
6 min readFeb 16, 2020

A React testing library tutorial — with examples!

Unit testing is the backbone of a web application, and in this article, I will share some examples of writing unit test cases with react-testing-library and help other developers to write test cases for their web app.

RTL focuses mainly on testing user experience, how your software will behave with the user. Also, the react team recommends using the react-testing-library to simulate user behavior in your tests.

Setup is very simple.

Configuration

Add a script in your package.json

Your jest.config.js will look like this

Jest-config

Dependencies:

npm install --save-dev @testing-library/react
npm install --save-dev @testing-library/jest-dom
npm run test

For more info, you can check setup instructions here
Also, we will use jest-dom to write tests that assert various things about the state of the DOM.

We will cover

1. A basic test case
2. Why do we need MockedProvider
3. Component using Context
4. Await from reacting testing library
5. How to mock window objects

A basic test case

import React from 'react';const MyComponent = (props) => (
const { logOut, loading } = props;
return (
<div data-testid="btnHeaderCategory">
{loading ? <div data-testid="loading"
styleName={{"color":"green"}} >Loading</div> :
<div data-testid="headerText" onClick={logOut} >
Mega menu
</div>
}
</div>
);
};
export default MyComponent;

To write the test case for the above component I will use some methods available from @testing-library/react like render and getByText.
Also, we will importtoBeInTheDocument from jest-dom to check if HTML is available in the document, below is the test case

import React from 'react';
import {toBeInTheDocument,toHaveStyle} from
'@testing-library/jest-dom';
import { render, getByText } from '@testing-library/react';
import MyComponent from '../MyComponent';
expect.extend({ toBeInTheDocument, toHaveStyle });const propData = {
loading: false,
data:[]
};
test('Test for loading state', () => {
const logOut = jest.fn() // function mock
render(
<MyComponent {...propData} logOut={logOut}/>
);
const loadingElem = getByText('Loading'); expect(loadingElem).toBeInTheDocument();
expect(loadingElem).toHaveStyle(`color:green`);
});

In the test file, we are rendering theMyComponent and passing some mock props to the component.

We have used methods like:
getByText which captures the element,
toBeInTheDocument will check for the HTML element in the DOM and toHaveStyle checks for the styling on the element.

There are various other methods available which you can use for your test cases, a lot can be found here.

Why do we need MockedProvider?

We will fetch some data for the component like name and twitter link, we will use useQuery from apollo-react-hooks.

import React from 'react';
import { useQuery } from '@apollo/react-hooks';
import ComponentQuery from './ComponentQuery.graphql';const MyComponent = () => (
const { data } = useQuery(ComponentQuery);
const userData = data?.userData;
return (
<div>
<span>{userData.name}</span>
<span>{userData.twitterLink}</span>
</div>
);
};
export default MyComponent;

Test case for the above scenario

import React from 'react';
import { toBeInTheDocument } from '@testing-library/jest-dom';
import { render, getByText } from '@testing-library/react';
import MyComponent from '../MyComponent';
import ComponentQuery from './ComponentQuery.graphql';
import { MockedProvider } from '@apollo/react-testing';expect.extend({ toBeInTheDocument});const mockData = {
request:{
query: ComponentQuery,
variables:{}
},
result: {
data: {
userData:{
name: "Aatif",
twitterLink: "https://twitter.com/aatifbandey",
__typename: 'DynamicUserData'
}
}
}
};
test('Test to check user name with mock data', () => {

const { container } = render(
<MockedProvider mocks={[mockData]}>
<MyComponent />
</MockedProvider>
);
const name = getByText(container, 'Aatif')
expect(name).toBeInTheDocument();
});

To test the behavior we have to provide mock data as per gql response so we will use MockedProvider from the apollo/react-testing library to pass the mock response for our GQL component
Note: mock should be exactly similar to the GQL query response.

Also, MockedProvider accepts one more prop called typeName which accepts a boolean value, if passed true mock response should contain a type-name.

Mocks are always passed as an array of objects if your components using multiple queries you can pass multiple mocks, the example shared below

<MockedProvider mocks={[mockData, mockDataNew]}> 
<MyComponent />
</MockedProvider>

Component using context

We will write a react component that uses a global context to get the name of the user.

import React, {useContext} from 'react';
import { GlobalContext } from './context/global';
const MyComponent = () => (
const [globalState] = useContext(GlobalContext);
const { name } = globalState; return (
<div>
<span>{name}</span>
</div>
);
};
export default MyComponent;

Test Case

import React from 'react';
import { toBeInTheDocument } from '@testing-library/jest-dom';
import { render, getByText } from '@testing-library/react';
import MyComponent from '../MyComponent';
import GlobalProvider from './context/global';expect.extend({ toBeInTheDocument});test('Test to check name from global context', () => {
const globalState = {
name: "Aatif"
};
const { container } = render(
<GlobalProvider value={globalState} >
<MyComponent />
</GlobalProvider>
);
const name = getByText(container, 'Aatif')
expect(name).toBeInTheDocument();
});

For the above test case, we should wrap the component with GlobalProvider and pass the global state value that is required via the actual component.

Await from reacting testing library

We have a component where we will lazy load some Cart component, based on prop data, we are using react-loadable to load our component.

import React from 'react';
import Loadable from 'react-loadable';
const Cart = Loadable(() => import(/* webpackChunkName: "lazy" */ './cart'));const MyComponent = (props) => (
const { showCart } = prop;
return (
<div>
<span>Lazy Load component</span>
{showCart ? <Cart /> : ""}
</div>
);
};
export default MyComponent;

Test case

import React from 'react';
import { toBeInTheDocument } from '@testing-library/jest-dom';
import { render,
waitForElement,
getByTestId
} from '@testing-library/react';
import MyComponent from '../MyComponent';expect.extend({ toBeInTheDocument});test('Test to load a lazy cart component', () => {

const { container } = render(
<MyComponent showCart={true} />
);
const elem = await waitForElement(
() => getByTestId(container, 'cartElem')
);
expect(elem).toBeInTheDocument();
});

In the above test case, we are checking if Cart component will be shown based on showCart prop, to run our test case we are using a method called waitForElement, this method will wait for DOM elements to appear, disappear, or change and as soon as the method complete a callback is called where we perform test operations.

Mocking Window Objects

We will write a component that will redirect a page on a button click, for redirection we are using window.location.assign method, below is the component

import React from 'react';const MyComponent = () => ( 
const redirect = () => {
window.location.assign("/checkout");
}
return (
<div>
<button onClick={redirect} data-testid={"checkOut"}>
Checkout
</button>
</div>
);
};
export default MyComponent;

Let’s write a test component that uses a window object, it's a little tricky, let see the test case first

import React from 'react';
import { toHaveBeenCalledWith } from '@testing-library/jest-dom';
import { render,
fireEvent,
getByTestId
} from '@testing-library/react';
import MyComponent from '../MyComponent';expect.extend({ toHaveBeenCalledWith });test('Test to load a lazy cart component', () => { jest.spyOn(window.location, 'assign').mockImplementation(l => {
expect(l).toEqual('/checkout');
});
const { container } = render(
<MyComponent />
);
const elem = getByTestId('checkOut');
fireEvent.click(elem);
expect(window.location.assign).toHaveBeenCalledWith('/checkout');});

We know that our test case runs don’t run on browsers, it runs on the NodeJS environments, so we don't have a window defined on node environment.

We will mock the window object as per the component specification, as you can see we are using jest.SpyOn method to mock,
To simulate a click on the button we have used the fireEvent method and to verify the test case we are using method toHaveBeenCalledWith to check redirection.

These were some basic examples of test cases using RTL and jest-dom, I hope you liked the article, share it with your friends and do follow me on twitter.

--

--