繁体   English   中英

useFakeTimers 在玩笑/测试库中不起作用

[英]useFakeTimers not working in jest/testing-library

我正在渲染一个使用 setTimeout 将内部文本从加载 state 更改为所需消息的元素:

function Message({ message }: any) {
  const [showMessage, setShowMessage] = useState(false);

  useEffect(() => {
    const CTATimer = setTimeout(() => {
      setShowMessage(true);
    }, 1500);
    return () => {
      clearTimeout(CTATimer);
    };
  }, []);

  if (!showMessage) {
    return <p>Loading...</p>;
  }

  return (
    <>
      <div>{message.text}</div>
    </>
  );
}

相应的测试呈现,然后将时间提前 1500 毫秒,然后应该显示消息。 但是,目前测试失败,终端显示文本仍在Loading... 测试是这样写的:

const mockMessage = {
  text: "this is a message",
  answers: [],
  id: 1,
};

afterEach(() => {
  jest.useRealTimers();
});

it("should show message after setTimeout", () => {
  jest.useFakeTimers();
  jest.advanceTimersByTime(1500);
  customRender(<Message message={mockMessage} />); // my customRender is just the default render but with a ThemeProvider wrapper.
  const message = screen.getByText(/this is a message/i);
  expect(message).toBeInTheDocument();
});

为什么我的测试在 1500 毫秒过去后仍然呈现加载 state?

您应该在渲染组件后提前计时器。 此外,您应该在act function 内调用jest.advanceTimersByTime() 。否则,它会抛出警告: Warning: An update to Message inside a test was not wrapped in act(...).

index.tsx

import React from 'react';
import { useEffect, useState } from 'react';

export function Message({ message }: any) {
  const [showMessage, setShowMessage] = useState(false);

  useEffect(() => {
    const CTATimer = setTimeout(() => {
      setShowMessage(true);
    }, 1500);
    return () => {
      clearTimeout(CTATimer);
    };
  }, []);

  if (!showMessage) {
    return <p>Loading...</p>;
  }

  return (
    <>
      <div>{message.text}</div>
    </>
  );
}

index.test.tsx

import React from 'react';
import { render, screen, act } from '@testing-library/react';
import '@testing-library/jest-dom/extend-expect';
import { Message } from './';

describe('Message', () => {
  const mockMessage = {
    text: 'this is a message',
    answers: [],
    id: 1,
  };

  afterEach(() => {
    jest.useRealTimers();
  });

  it('should show message after setTimeout', () => {
    jest.useFakeTimers();
    render(<Message message={mockMessage} />);
    act(() => {
      jest.advanceTimersByTime(1500);
    });
    const message = screen.getByText(/this is a message/i);
    expect(message).toBeInTheDocument();
  });
});

测试结果:

 PASS  stackoverflow/71174071/index.test.tsx (9.705 s)
  Message
    ✓ should show message after setTimeout (27 ms)

-----------|---------|----------|---------|---------|-------------------
File       | % Stmts | % Branch | % Funcs | % Lines | Uncovered Line #s 
-----------|---------|----------|---------|---------|-------------------
All files  |     100 |      100 |     100 |     100 |                   
 index.tsx |     100 |      100 |     100 |     100 |                   
-----------|---------|----------|---------|---------|-------------------
Test Suites: 1 passed, 1 total
Tests:       1 passed, 1 total
Snapshots:   0 total
Time:        10.903 s
Ran all test suites related to changed files.

如果因为需要使用userEvent进行打字等而使用异步测试。我在这个博客上找到了解决方案: https://onestepcode.com/testing-library-user-event-with-fake-timers/

诀窍是将userEvent上的delay选项设置为null

const user = userEvent.setup({ delay: null });

这是一个完整的测试用例

test("Pressing the button hides the text (fake timers)", async () => {
    const user = userEvent.setup({ delay: null });
    jest.useFakeTimers();
    
    render(<Demo />);

    const button = screen.getByRole("button");
    await user.click(button);

    act(() => {
        jest.runAllTimers();
    });

    const text = screen.queryByText("Hello World!");
    expect(text).not.toBeInTheDocument();

    jest.useRealTimers();
});

暂无
暂无

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

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