【问题标题】:Can't test effects of resolved promise无法测试已解决承诺的效果
【发布时间】:2017-05-23 09:51:02
【问题描述】:

我在用户中有以下代码:

 import { redirectTo } from 'urlUtils';
 export function create(user) {
    api.postUser(user).then((response) => {
      redirectTo(response.userUrl);
    })
 }

我有以下测试:

import * as api from 'api'
import * as user from 'user'

sinon.stub(api, 'postUser').returns(
  Promise.resolve({ userUrl: 'a-url' })
);
sinon.spy(urlUtils, 'redirectTo');
const userData = {id: 2};

user.create(userData);
expect(api.postUser.calledWith(userData)).toBe(true);  // This passes
expect(urlUtils.redirectTo.calledOnce).toBe(true); // This fails

我已经能够在浏览器上对其进行测试,并且重定向正在发生。我在这里想念什么?我已经存根请求调用以同步解决承诺,所以这不应该是一个问题。

【问题讨论】:

  • promise 是异步的 - 你不需要等待它解决
  • 但是我正在处理和解决 Promise,正如您在以下位置看到的:sinon.stub(api, 'poserUser').returns( Promise.resolve({ userUrl: 'a-url' }) );
  • 但是,promise 仍然是异步的
  • 那么,测试这些场景的最佳方法是什么?
  • sinon.stub(api, 'poserUser') 是错字吗?不应该是“postUser”吗?

标签: javascript testing promise mocha.js sinon


【解决方案1】:

Promise 是异步的,所以当您执行 expect(urlUtils.redirectTo.calledOnce).toBe(true) 时,promise 尚未解决。

解决它的最简单方法是将该期望包装在一个短暂的超时中,然后使用您正在使用的测试框架提供的任何实用程序来发出异步测试已完成的信号。像这样的:

setTimeout(() => {
  expect(urlUtils.redirectTo.calledOnce).toBe(true);
  done();
}, 5);

另一个更好的解决方案是实际使用存根返回的承诺。首先,保留对该承诺的引用:

替换:

sinon.stub(api, 'postUser').returns(
  Promise.resolve({ userUrl: 'a-url' })
);

与:

const postUserPromise = Promise.resolve({ userUrl: 'a-url' });
sinon.stub(api, 'postUser').returns(postUserPromise);

然后这样写你的期望:

postUserPromise.then(() => {
  expect(urlUtils.redirectTo.calledOnce).toBe(true);
  done();
});

done() 是大多数测试框架(据我所知至少是 Jasmine 和 Mocha)提供的用于表示异步测试已完成的功能。您将它作为定义测试的函数的第一个参数,并通过在函数签名中指定它来告诉测试框架您的测试是异步的。

例子:

it("is a synchronous test, completed when test function returns", () => {
  expect(true).to.equal(true);
});

it("is an asynchronous test, done() must be called for it to complete", (done) => {
  setTimeout(() => {
    expect(true).to.equal(true);
    done();
  }, 5000);
});

【讨论】:

  • 我正在处理和解决承诺,如您所见:sinon.stub(api, 'poserUser').returns( Promise.resolve({ userUrl: 'a-url' }) );
  • @HommerSmith 即使它“立即”解决,promise 仍然是异步的,即回调的调用被推迟到线程空闲时
  • 在增加测试套件的时间方面不是很糟糕吗?
  • @HommerSmith 我们只是在谈论几毫秒,这只是为了延迟期望以确保在已经延迟的 promise 回调之后调用它。有一个更好的选择,但它有点复杂,我会用它来修改我的答案。
  • done() 是您的测试框架为异步测试提供的功能,它是您用来告诉测试框架测试已完成并且应该继续下一个测试的功能。我在答案中添加了描述。根据不同的测试框架,这可能会有所不同。我无法从您的代码中看出您使用的是哪个框架。
猜你喜欢
  • 2019-09-01
  • 2018-04-02
  • 1970-01-01
  • 1970-01-01
  • 2017-07-22
  • 2015-09-22
  • 2021-05-25
  • 1970-01-01
  • 2020-10-21
相关资源
最近更新 更多