【问题标题】:Unexpected afterAll execution in Jest/Testing Library test suite在 Jest/Testing Library 测试套件中意外的 afterAll 执行
【发布时间】:2021-03-02 04:25:41
【问题描述】:

我正在测试 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 警告的原因?

【问题讨论】:

    标签: javascript reactjs unit-testing jestjs react-testing-library


    【解决方案1】:

    警告表示被测组件具有一些测试未等待的异步副作用(在此测试套件中由 fetch 表示)。

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

    正如与测试框架的集成所期望的那样,React 测试库会进行清理以使测试不会相互影响,afterEach 是这样做的正确位置。正如the documentation 所说,

    如果您使用的测试框架支持 afterEach 全局变量(如 mocha、Jest 和 Jasmine),则默认情况下会在每次测试后自动调用 Cleanup。

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

    另外,await waitFor(() =&gt; render(&lt;App /&gt;)) 也没什么用,因为它只对抛出错误的函数有效,最常见的是断言。可以简化为render(&lt;App /&gt;)

    【讨论】:

    • 这是一个非常好的答案!我要补充一点,可能没有理由为承诺添加延迟——你可以简单地使用global.fetch.mockResolvedValue({ ok: true });。如果您真的想保留setTimeout,可以查看jest.useFakeTimers(); 以防止降低测试速度。
    • 我很欣赏真正全面的信息!它帮助我更好地理解afterAllafterEach 的行为。就await waitFor(() =&gt; render(&lt;App /&gt;)) 而言,这只是为了防止act(...) 警告测试抛出否则。
    • @Doug 我正在使用setTimeout 来模拟解决承诺以测试加载状态的延迟。为了消逝的时间,这是为了在模拟 Promise 解析之前捕获加载状态而消逝的时间。
    • @Derek 很高兴它有帮助。在这种情况下,waitFor 是一种解决方法,因为它在内部使用了 act,因此可能会出现 act 警告,因为它确实需要,可能应该是 await act(() =&gt; render(&lt;App /&gt;))。对于不涉及其他 setTimeout 等的任何情况,setTimeout 0 就足够了。而且大多数情况下,使用 promise 和没有 setTimeout 来模拟异步函数就足够了,组件生命周期没有会导致竞争条件的延迟,尤其是对于测试渲染器这是同步的。
    • @EstusFlask await act(() =&gt; render(&lt;App /&gt;)) 会引发多个警告 The callback passed to act(...) function must return undefined, or a Promise.An update to App inside a test was not wrapped in act(...).Do not await the result of calling act(...) with sync logic, it is not a Promise. waitFor 不会引发这些警告。
    猜你喜欢
    • 2020-09-03
    • 2019-07-08
    • 2020-01-20
    • 1970-01-01
    • 1970-01-01
    • 2021-12-29
    • 2021-02-28
    • 2020-03-22
    • 2020-05-14
    相关资源
    最近更新 更多