【问题标题】:Can I use async/await in Jest, when the tested function is not using async/await?当测试的函数没有使用 async/await 时,我可以在 Jest 中使用 async/await 吗?
【发布时间】:2022-01-17 07:17:54
【问题描述】:

我有一个用于添加新用户的简单控制器。解析成功(用户添加)后,控制器发送 202 响应。如您所见,该函数使用的是then/catch,而不是使用async/await。

const addUserController = function (req, res, next) {
    Users.addOne(req.userid, req.body.email)
    .then(() => {
      res.status(202).send();
    })
    .catch((err) => {
      console.log(err);
      res.status(500).json({ message: "Internal server error." });
    });
};

当我在 Jest 中使用 the 测试这个函数时,该函数立即执行,没有转到 then() 部分,导致错误的 200 代码,而不是 202,因此以下测试失败:

it("Should add a user", () => {
    let req, res, next, pool;
    pool = new Pool();
    req = httpsMocks.createRequest();
    res = httpsMocks.createResponse();
    res.next = null;
    req.userid = 1;
    req.body = {
      id: 2
    }
    pool.query.mockResolvedValue({rows:[], rowCount: 1});
    apiController.addUserController(req, res, next);
    expect(res.statusCode).toBe(202);
    expect(pool.query).toBeCalledTimes(1);
});

但是,当我这样做时:

it("Should add a user", async () => {
    let req, res, next, pool;
    pool = new Pool();
    req = httpsMocks.createRequest();
    res = httpsMocks.createResponse();
    res.next = null;
    req.userid = 1;
    req.body = {
      id: 2
    }
    pool.query.mockResolvedValue({rows:[], rowCount: 1});
    await apiController.addUserController(req, res, next);
    expect(res.statusCode).toBe(202);
    expect(pool.query).toBeCalledTimes(1);
});

那就是我添加了 async/await,它工作正常 - 响应状态码是 202,表示该函数已等待并且测试通过。 但为什么?当我将鼠标悬停在新添加的“等待”VS 代码上时,提示

'await' 对这个表达式的类型没有影响。

这是有道理的——它应该没有效果,因为测试的函数不是异步的,所以它不应该工作,但是它可以工作——只有当我将 async/await 添加到 Jest 函数时它才能正常工作.

谁能给我解释一下?

【问题讨论】:

  • addUserController 不返回承诺,因此使用 await 不会做任何事情(afaik)
  • 但是在运行 expect() 测试之前,我能做些什么来等待 addUserController 执行?因为显然,在当前阶段,addUserController 只是返回 undefined。我知道我可以将 addUserController 重构为 async/await,然后也使 Jest 中的函数异步/await,但我想了解这里发生了什么以及如何以当前形式解决这个问题
  • httpsMocks 是什么,您是否为此使用了库?
  • 是的,就是这个库:npmjs.com/package/node-mocks-http

标签: javascript node.js testing


【解决方案1】:

我添加了 async/await,它工作正常 - 响应状态代码为 202,表示该函数已等待并且测试通过。但为什么呢?

不,正如您从缺少的返回值中得出的结论,该函数未被等待。你的代码相当于

apiController.addUserController(req, res, next);
await undefined;

现在,为什么它仍然有所作为?因为使用await,测试在运行expect() 调用之前会等待一小段时间,而这一小段位足以让您的模拟池返回一个值并执行第一个.then() 处理程序。

但是,您现在基本上引入了竞争条件。在addUserController 中有更长的承诺链会使测试失败。甚至对 .then().catch() 中创建的 500 状态的测试可能已经无法正常工作。

这很脆弱,不要写这样的测试。一种解决方案是简单地将return 来自addUserController 和await 的承诺链 - 只要没有其他调用者对此感到困惑,这是简单的解决方法。另一种解决方案是实际等待模拟响应实际发送。如果我读了docs of node-mocks-http 正确,这样的东西应该可以工作:

it("Should add a user", async () => {
    const { once, EventEmitter } = require('events');
    const pool = new Pool();
    const req = httpsMocks.createRequest();
    const res = httpsMocks.createResponse({ eventEmitter: EventEmitter });
    res.next = null;
    req.userid = 1;
    req.body = {
      id: 2
    }
    pool.query.mockResolvedValue({rows:[], rowCount: 1});
    const responseEndPromise = once(res, 'end');
    
    apiController.addUserController(req, res, next);
    await responseEndPromise;

    expect(res.statusCode).toBe(202);
    expect(pool.query).toBeCalledTimes(1);
});

【讨论】:

  • 好的,感谢您解释为什么会这样。是否可以在 Jest 测试函数中等待 addUserController() 的解析,而不将其重构为 async/await?
  • 是的,你只需要return函数的承诺。
猜你喜欢
  • 2020-10-12
  • 2016-07-21
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2020-02-20
  • 2020-08-01
  • 1970-01-01
  • 2016-02-24
相关资源
最近更新 更多