Test your React App with react testing library and jest-dom
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
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.