使用 useEffect 和 jest.useFakeTimers() 进行测试

Posted

技术标签:

【中文标题】使用 useEffect 和 jest.useFakeTimers() 进行测试【英文标题】:Testing with useEffect and jest.useFakeTimer() 【发布时间】:2019-09-25 05:25:03 【问题描述】:

我正在尝试在这里创建简单的复习组件

Refresher.js

import  useEffect  from 'react';

const Refresher = ( onRefresh ) => 
  useEffect(() => 
    const id = setInterval(onRefresh, 60000);
    return () => 
      clearInterval(id);
    ;
  , [onRefresh]);

  return null;
;

export default Refresher;

但是,当我尝试使用 jest.useFakeTimers() 对其进行测试时,不知何故它不起作用。即使在jest.runOnlyPendingTimers() 之后也不会调用存根

import React from 'react';
import renderer from 'react-test-renderer';
import Refresher from '../Refresher';

describe('Refresher', () => 
  test('should refresh the result every 60 seconds', () => 
    jest.useFakeTimers();

    const onRefreshSpy = jest.fn();

    const refresher = renderer.create(<Refresher onRefresh=onRefreshSpy />);

    expect(onRefreshSpy).not.toHaveBeenCalled();
    jest.runOnlyPendingTimers();
    refresher.update(); // Trying force update here

    expect(onRefreshSpy).toHaveBeenCalled();
  );
);

如果没记错的话,如果组件没有更新,间隔将不会运行,所以我尝试使用refresher.update(),但似乎它并没有真正起作用。

有人知道如何在这里修复测试吗?

【问题讨论】:

【参考方案1】:

您只需要模拟 useLayoutEffect 而不是 useEffect。见问题here

describe('Refresher', () => 
  beforeAll(() => jest.spyOn(React, 'useEffect').mockImplementation(React.useLayoutEffect))
  test('should refresh the result every 60 seconds', () => 
    jest.useFakeTimers();
    const onRefreshSpy = jest.fn();
    const refresher = renderer.create(<Refresher onRefresh=onRefreshSpy />);
    expect(onRefreshSpy).not.toHaveBeenCalled();
    jest.runOnlyPendingTimers();
    expect(onRefreshSpy).toHaveBeenCalled();
  );
);

【讨论】:

太棒了!这很有帮助,它可以与额外的afterEach 清理一起工作,但似乎这不是测试useEffect 的好方法,希望我们能有一个合适的解决方案【参考方案2】:

您可以更好地控制modern 假计时器。

import React from 'react';
import renderer from 'react-test-renderer';
import Refresher from '../Refresher';

describe('Refresher', () => 
  test('should refresh the result every 60 seconds', () => 
    jest.useFakeTimers('modern'); // default for Jest 27+

    const onRefreshSpy = jest.fn();

    const refresher = renderer.create(<Refresher onRefresh=onRefreshSpy />);

    expect(onRefreshSpy).not.toHaveBeenCalled();

    jest.advanceTimersByTime(60000);
    expect(onRefreshSpy).toHaveBeenCalledTimes(1);

    jest.advanceTimersByTime(60000);
    expect(onRefreshSpy).toHaveBeenCalledTimes(2);
  );
);

【讨论】:

以上是关于使用 useEffect 和 jest.useFakeTimers() 进行测试的主要内容,如果未能解决你的问题,请参考以下文章

useState和useEffect

使用 React Native 和 useEffect 获取

useEffect和useLayoutEffect

使用 useEffect 和 setInterval 一个接一个地打印一个字母

如何测试使用 Context 和 useEffect 的 React.js 页面?

使用自定义钩子封装 useEffect 和 AJAX 请求