Jest/Testing 库测试套件中的意外 afterAll 执行

Unexpected afterAll execution in Jest/Testing Library test suite

我正在测试 React 组件中的一些异步行为,包括加载和错误状态。我通过使用设置状态的不同 describe 块来接近不同的状态,如下所示:

jest.spyOn(global, 'fetch');

describe('The App', () => {
  const promiseWithDelay = (json, delay=0) =>
    new Promise(resolve =>
      setTimeout(
        () => resolve({
          ok: true,
          json: () => json
        }), delay
      )
    );

  describe('given data loading', () => {
    beforeEach(async () => {
      global.fetch.mockImplementationOnce(() =>
        promiseWithDelay(someData, 50)
      );

      await waitFor(() => render(<App />));
    });

    // ... successful tests for a loading state here
  });

  describe('some other tests', () => {
    // ... more setup and other tests here
  });
});

我正在通过模拟创建加载状态 fetch

测试全部通过,但我得到 Warning: Can't perform a React state update on an unmounted component. 错误,我认为这是因为第一个描述块中的 render 在第二个 describe 块开始 运行.

为了清除错误,我原本想在第一个describe块中包含一个afterAll函数来等待创建加载状态的Promise解析,像这样:

describe('The App', () => {
  // ... etc

  describe('given data loading', () => {
    beforeEach(async () => {
      // ... etc
    });

    // ... successful tests for a loading state here

    afterAll(async () => {
      await waitFor(() => {
        expect(screen.queryByTestId('some-rendered-content')).toBeInTheDocument();
      });
    });
  });

  describe('some other tests', () => {
    // ... more setup and other tests here
  });
});

这并没有清除错误,但是将 afterAll 函数更改为 afterEach 函数就可以了。

完全困惑,我添加了一些控制台日志记录以查看何时发生了什么,并发现了这一点:

// afterEach console logs

beforeEach: Starting loading test
test: Testing loading
afterEach: Cleaning up from loading test
afterEach: Waiting for content to render
afterEach: Waiting for content to render
afterEach: Cleaned up from loading test
some other test: Starting next test
// afterAll console logs

beforeEach: Starting loading test
test: Testing loading
afterAll: Cleaning up from loading test
afterAll: Waiting for content to render
(console.error): Warning: Can't perform a React state update on an unmounted component. (etc.)
afterAll: Waiting for content to render
afterAll: Waiting for content to render
... afterAll: Waiting for content to render logs many times here
some other test: Starting next test

谁能解释这里的操作顺序以及可能导致 afterAll 而不是 afterEach 警告的原因?

该警告意味着被测组件具有异步副作用(在此测试套件中由 fetch 表示),某些测试未等待这些副作用。

afterAll 应用于各自 describe 内的一组测试,如果有多个测试,其中一些不会受到 waitForafterAll 提供。

正如与测试框架的集成所预期的那样,React Testing Library 进行了清理以使测试不相互影响,afterEach 是正确的地方。正如 the documentation 所述,

Cleanup is called after each test automatically by default if the testing framework you're using supports the afterEach global (like mocha, Jest, and Jasmine).

编写测试的正确方法是在 RTL 清理之前等待组件内部的副作用完成。这需要在每个测试中等待至少与副作用持续时间(50 毫秒)相同的时间,并且需要更长的时间才能刷新承诺。 waitFor 的使用允许不依赖实现细节。由于副作用会影响测试结果,waitFor 的正确位置是 beforeEach 或测试本身,而不是 afterEachafterAll

此外 await waitFor(() => render(<App />)) 没有任何用处,因为它只对抛出错误的函数有效,最常见的是断言。可以简化为render(<App />).