【问题标题】:How can I run an assertion test after a certain amount of simulated time using jest?如何使用 jest 在一定的模拟时间后运行断言测试?
【发布时间】:2016-11-22 21:00:17
【问题描述】:

假设我有一个组件,它呈现一个简单的 div,其中包含一个从 0 开始并每秒递增 1 的整数。所以 5 秒后,组件应该呈现“5”,30 秒后,它应该呈现“30”,依此类推。如果我想测试这个组件并确保它在 5 秒后呈现应有的效果,我可能会写这样的东西。

it('should render <5> after 5 seconds', () => {
  const time = mount(<Timer/>)
  setTimeout(() => {
    expect(time.text()).toEqual('5')
  }, 5000)
})

但是,这不起作用,因为测试从不实际运行期望,并且不管任何事情都返回通过。即使它确实有效,使用这样的超时也会非常低效,因为测试必须等待 5 秒。如果我想模拟更多的时间怎么办?经过一番搜索,我发现 jest 实际上有一个 timer mock 但我似乎无法弄清楚如何在这种情况下实现它。任何帮助将不胜感激。谢谢!

【问题讨论】:

  • 可以分享一下使用定时器的模块代码吗?

标签: javascript unit-testing reactjs jestjs enzyme


【解决方案1】:

为了让 Jest 知道您的测试是异步的,您需要:1) 返回一个 Promise,或 2) 在测试回调中声明一个参数,如下所示:

it('should render <5> after 5 seconds', done => {
  // test stuff
});

来源:https://facebook.github.io/jest/docs/en/asynchronous.html

为了让您的测试正常工作:

it('should render <5> after 5 seconds', done => {
  jest.useFakeTimers(); // this must be called before any async things happen

  const time = mount(<Timer/>);

  setTimeout(() => {
    // Note the placement of this try/catch is important.
    // You'd think it could be placed most anywhere, but nope...
    try {
      // If this assertion fails, an err is thrown.
      // If we do not catch()...done.fail(e) it,
      // then this test will take jasmine.DEFAULT_TIMEOUT_INTERVAL (5s unless you override)
      // and then fail with an unhelpful message.
      expect(time.text()).toEqual('5');
      done();
    } catch(e) {
      done.fail(e);
    }
  }, 5000);
  jest.runTimersToTime(5000); // This basically fast forwards 5s.
});

【讨论】:

  • 这些 try/catch 写起来很麻烦,所以我做了一些测试工具,希望尽快发布。除此之外,它还可以将 setTimeout() 的回调正文缩短为:tf.finish(done, () =&gt; expect(time.text()).toEqual('3')); 如果您有兴趣,请告诉我,我会努力将其发布!
【解决方案2】:

您必须在“require”组件之前调用jest.useFakeTimers(); 来模拟setTimeout。

这里我们通过调用 jest.useFakeTimers(); 来启用假定时器。这个 使用模拟函数模拟 setTimeout 和其他计时器函数。

要测试异步工作流Jest documentation says,您必须为每个“it”函数回调返回一个Promise

要测试一个异步函数,只需从它返回一个 Promise。什么时候 运行测试,Jest 将等待 promise 解决之前 让测试完成。你也可以从 beforeEach、afterEach、beforeAll 或 afterAll 函数。例如, 假设 fetchBeverageList() 返回一个应该 解决一个包含柠檬的列表。您可以使用以下方法进行测试:


所以你可以这样写:

it('should render <5> after 5 seconds', () => {
  jest.useFakeTimers();
  //only after that, require your componenet
  let Timer = require('./timer');
  const time = mount(<Timer/>);
  jest.runAllTimers();
  expect(setTimeout.mock.calls.length).toBe(1);
  expect(setTimeout.mock.calls[0][1]).toBe(5000);
  expect(time.text()).toEqual('5');
})

【讨论】:

  • 谢谢。虽然我仍然不确定当异步函数是期望调用本身时,promise 应该是什么样子。
  • 您不需要,因为如果您模拟 settimeout,您的测试将同步运行。我写了两个答案,因为您提出了两个问题:)
  • 再次感谢!我试过这个,但我得到了错误“运行了 100000 个定时器,还有更多!假设我们已经达到了无限递归并退出......”也许这是因为 Timer 组件的间隔每 1000 毫秒运行一次?跨度>
  • "也许这是因为 Timer 组件有一个每 1000 毫秒运行一次的时间间隔?"确切地!这就是为什么对于 setTimeout/setInterval 的组合,我通常更喜欢 jest.runTimersToTime(),就像我的回答一样。
猜你喜欢
  • 2020-03-02
  • 2020-05-15
  • 2019-05-19
  • 1970-01-01
  • 1970-01-01
  • 2017-02-13
  • 2020-05-28
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多