简体   繁体   English

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

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

I'm rendering an element that makes use of a setTimeout to change the inner text from a loading state to a desired message:我正在渲染一个使用 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>
    </>
  );
}

The corresponding test renders, then advances time by 1500ms, and then should show the message.相应的测试呈现,然后将时间提前 1500 毫秒,然后应该显示消息。 However, currently the test fails and the terminal shows that the text is still Loading... .但是,目前测试失败,终端显示文本仍在Loading... The test is written like so:测试是这样写的:

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();
});

Why would my test still be rendering the loading state when 1500ms have passed?为什么我的测试在 1500 毫秒过去后仍然呈现加载 state?

You should advance timers after rendering the component.您应该在渲染组件后提前计时器。 Besides, you should call jest.advanceTimersByTime() inside act function. Otherwise, it will throws an warning: Warning: An update to Message inside a test was not wrapped in act(...).此外,您应该在act function 内调用jest.advanceTimersByTime() 。否则,它会抛出警告: Warning: An update to Message inside a test was not wrapped in act(...).

index.tsx : 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 : 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();
  });
});

Test result:测试结果:

 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.

If working with an asynchronous test because you need to use userEvent for typing etc. I found a solution on this blog: https://onestepcode.com/testing-library-user-event-with-fake-timers/如果因为需要使用userEvent进行打字等而使用异步测试。我在这个博客上找到了解决方案: https://onestepcode.com/testing-library-user-event-with-fake-timers/

The trick is to set the delay option on the userEvent to null .诀窍是将userEvent上的delay选项设置为null

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

Here is a full test case这是一个完整的测试用例

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.

相关问题 使用 fireEvent 传递自定义事件属性(testing-library 和 jest) - pass custom event properties with fireEvent (testing-library and jest) 使用 Jest 和 @testing-library/react-hooks 在 TypeScript 中单元测试自定义 React Hook - Unit Testing Custom React Hooks in TypeScript using Jest and @testing-library/react-hooks 如何将@testing-library/jest-dom 添加到 svelte 中的每个测试文件中? - How to add @testing-library/jest-dom to every testing file in svelte? 自定义 React 组件库 - 开玩笑“找不到模块反应”- 测试库,汇总 - Custom React Component Library - jest 'cannot find module react'- testing-library, rollup 为什么jest.useFakeTimers不能使用RxJs Observable延迟 - Why is jest.useFakeTimers not working with RxJs Observable delay 测试库自定义查询打字稿错误 - Testing-library custom query Typescript error Jest mock 不与 React 测试库一起使用 - Jest mock not working with React Testing Library MonacoEditor 不使用 Jest 和 React 测试库 - MonacoEditor not working with Jest and React Testing Library 尝试在 Jest 中模拟 setInterval(),使用 jest.useFakeTimers(),未按预期工作,断言导致类型匹配器错误 - Attempting to mock setInterval() in Jest, using jest.useFakeTimers(),not working as expected and assertions result in type matcher errors 如何使用 Cypress 和 @testing-library/cypress 测试复选框切换, - How to test checkbox toggle with Cypress and @testing-library/cypress ,
 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM