开玩笑地期望 Promise *不*完成

aka*_*ppi 5 timeout promise jestjs

我有以下需要测试是否有事情不会发生。

虽然测试类似的东西可能值得讨论(等待多长时间才足够?),但我希望 Jest 中存在更好的方法来与测试超时集成。到目前为止,我还没有找到,但让我们开始测试吧。

test ('User information is not distributed to a project where the user is not a member', async () => {

  // Write in 'userInfo' -> should NOT turn up in project 1.
  //
  await collection("userInfo").doc("xyz").set({ displayName: "blah", photoURL: "https://no-such.png" });

  // (firebase-jest-testing 0.0.3-beta.3)
  await expect( eventually("projects/1/userInfo/xyz", o => !!o, 800 /*ms*/) ).resolves.toBeUndefined();

  // ideally:
  //await expect(prom).not.toComplete;    // ..but with cancelling such a promise

}, 9999 /*ms*/ );
Run Code Online (Sandbox Code Playgroud)

返回eventuallya Promise,我想检查一下:

  • 在测试的正常超时时间内...
    • 这样的 Promise 未完成(解决或拒绝)

Jest 提供了.resolves.rejects没有将两者结合起来的东西。

  1. 我可以.not.toComplete使用一些 Jest 扩展机制来创建预期的内容吗?
  2. 我可以创建一个“在测试超时之前运行”(能够使测试通过或失败)触发器吗?

我认为这个2.建议可能会很方便,并且可以为此创建一个功能请求,但让我们看看这会得到什么评论。


编辑:还有一个更复杂的地方,那就是 JS Promise 不能从外部取消(但它们可以从内部超时)。

aka*_*ppi 5

我最终用自定义匹配器解决了这个问题:

/*
* test-fns/matchers/timesOut.js
*
* Usage:
*   <<
*     expect(prom).timesOut(500);
*   <<
*/
import { expect } from '@jest/globals'

expect.extend({
  async timesOut(prom, ms) {   // (Promise of any, number) => { message: () => string, pass: boolean }

    // Wait for either 'prom' to complete, or a timeout.
    //
    const [resolved,error] = await Promise.race([ prom, timeoutMs(ms) ])
      .then(x => [x])
      .catch(err => [undefined,err] );

    const pass = (resolved === TIMED_OUT);

    return pass ? {
      message: () => `expected not to time out in ${ms}ms`,
      pass: true
    } : {
      message: () => `expected to time out in ${ms}ms, but ${ error ? `rejected with ${error}`:`resolved with ${resolved}` }`,
      pass: false
    }
  }
})

const timeoutMs = (ms) => new Promise((resolve) => { setTimeout(resolve, ms); })
  .then( _ => TIMED_OUT);

const TIMED_OUT = Symbol()
Run Code Online (Sandbox Code Playgroud)

来源

好的一面是,这可以添加到任何 Jest 项目中。

缺点是,需要单独提及延迟(并保证 Jest 的超时不会发生在之前)。

使问题的代码变成:

await expect( eventually("projects/1/userInfo/xyz") ).timesOut(300)
Run Code Online (Sandbox Code Playgroud)

Firebase 用户注意事项:

如果 Firestore JS SDK 客户端侦听器仍处于活动状态,Jest 不会退出到操作系统级别。您可以通过取消订阅来防止这种情况发生afterAll- 但这意味着要跟踪哪些听众还活着,哪些听众没有。图书馆firebase-jest-testing在幕后为你做这件事。另外,这个问题最终会被 Firebase 修复。