My tests are affecting each other. I'm using the default create-react-app setup with Typescript. All tests run fine individually but when I run all tests the last one fails (both in IntelliJ and npm test
). The assertion that fails finds a value that was caused by the previous test.
Now I have read articles such as Test Isolation with React but I am not sharing any values between my tests. I also read about the cleanUp function and tried adding beforeEach(cleanup) and beforeAll(cleanUp), but I didn't found a working solution yet besides putting each test in a separate file. I feel the solution should be pretty simple.
I've quickly generated a create-react-app with TypeScript to reproduce the issue in a small as possible project: https://github.com/Leejjon/breakingtests
My App.tsx
import React from 'react';
import './App.css';
import {BrowserRouter, Link, Route} from 'react-router-dom';
const About: React.FC = () => {
return (
<div>
<h1 id="pageHeader">About page</h1>
<p>This is the about page</p>
</div>
);
};
const Home: React.FC = () => {
return (
<div>
<h1 id="pageHeader">Home page</h1>
<p>This is the home page</p>
</div>
);
};
const News: React.FC = () => {
return (
<div>
<h1 id="pageHeader">News page</h1>
<p>This is the news page</p>
</div>
);
};
const App: React.FC = () => {
return (
<div className="App">
<BrowserRouter>
<Link id="linkToHome" to="/">Home</Link><br/>
<Link id="linkToNews" to="/news">News</Link><br/>
<Link id="linkToAbout" to="/about">About</Link>
<Route exact path="/" component={Home}/>
<Route exact path="/news" component={News}/>
<Route exact path="/about" component={About}/>
</BrowserRouter>
</div>
);
};
export default App;
My App.test.tsx:
import React from 'react';
import {render, fireEvent, waitForElement} from '@testing-library/react';
import App from './App';
describe('Test routing', () => {
test('Verify home page content', () => {
const {container} = render(<App/>);
const pageHeaderContent = container.querySelector("#pageHeader")
?.firstChild
?.textContent;
expect(pageHeaderContent).toMatch('Home page');
});
test('Navigate to news', async () => {
const {container} = render(<App/>);
const pageHeaderContent = container.querySelector("#pageHeader")
?.firstChild
?.textContent;
expect(pageHeaderContent).toMatch('Home page');
const linkToNewsElement: Element = (container.querySelector('#linkToNews') as Element);
fireEvent.click(linkToNewsElement);
const pageHeaderContentAfterClick = await waitForElement(() => container.querySelector('#pageHeader')?.firstChild?.textContent);
expect(pageHeaderContentAfterClick).toMatch('News page');
});
test('Navigate to about', async () => {
const {container} = render(<App/>);
const pageHeaderContent = container.querySelector("#pageHeader")
?.firstChild
?.textContent;
expect(pageHeaderContent).toMatch('Home page');
const linkToAboutElement: Element = (container.querySelector('#linkToAbout') as Element);
fireEvent.click(linkToAboutElement);
const pageHeaderContentAfterClick = await waitForElement(() => container.querySelector('#pageHeader')?.firstChild?.textContent);
expect(pageHeaderContentAfterClick).toMatch('About page');
});
});
I found out by adding console.log(document.location.href);
that the location is not reset. Which makes sense.
The code below resets the url. I could enter any domain to fix my tests, for example http://blabla/
will also work.
beforeEach(() => {
delete window.location;
// @ts-ignore
window.location = new URL('http://localhost/');
});
In TypeScript this gives an error: TS2739: Type 'URL' is missing the following properties from type 'Location': ancestorOrigins, assign, reload, replace
. I didn't know how to fix this so I suppressed it it for now.
EDIT:
cleanup Unmounts React trees that were mounted with render, but doesn't reset state from stores/reducers. The solution I took for this situation was to create a reset function in my store and call it at the beginning of each test.
resetStore: () => {
set(initialState);
},
and call it in your test file
beforeEach(() => {
resetStore();
});
If you're using mocha, Jest, or Jasmine, the cleanup will be done automatically, but you need to put your render
in a beforeEach
to recreate it for every test.
let container;
beforeEach(() => {
const app = render(<App/>);
container = app.container
});
If you use another testing framework, you'll need to cleanup
manually like so
import { cleanup, render } from '@testing-library/react'
import test from 'ava'
test.afterEach(cleanup)
The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.