简体   繁体   English

如何在 React 测试库中隔离测试?

[英]How to isolate tests in React Testing Library?

My tests are affecting each other.我的测试相互影响。 I'm using the default create-react-app setup with Typescript.我在 Typescript 中使用默认的 create-react-app 设置。 All tests run fine individually but when I run all tests the last one fails (both in IntelliJ and npm test ).所有测试单独运行良好,但是当我运行所有测试时,最后一个失败(在 IntelliJ 和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.现在我已经阅读了诸如Test Isolation with React 之类的文章,但我没有在我的测试之间共享任何值。 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.我还阅读了 cleanUp 函数并尝试添加 beforeEach(cleanup) 和 beforeAll(cleanUp),但除了将每个测试放在单独的文件中之外,我还没有找到可行的解决方案。 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我已经使用 TypeScript 快速生成了一个 create-react-app,以在一个尽可能小的项目中重现该问题: https : //github.com/Leejjon/breaktests

My App.tsx我的应用程序.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:我的 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);我通过添加console.log(document.location.href); that the location is not reset.该位置未重置。 Which makes sense.这是有道理的。

The code below resets the url.下面的代码重置了 url。 I could enter any domain to fix my tests, for example http://blabla/ will also work.我可以输入任何域来修复我的测试,例如http://blabla/也可以。

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 .在 TypeScript 中,这给出了一个错误: 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. cleanup 卸载与渲染一起安装的 React 树,但不会从商店/减速器重置状态。 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.如果您使用 mocha、Jest 或 Jasmine,清理将自动完成,但您需要将render放在beforeEach中,以便为每个测试重新创建它。

let container;

beforeEach(() => {
  const app = render(<App/>);
  container = app.container
});

If you use another testing framework, you'll need to cleanup manually like so如果您使用其他测试框架,则需要像这样手动cleanup

import { cleanup, render } from '@testing-library/react'
import test from 'ava'

test.afterEach(cleanup)

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM