我如何断言一个函数已经使用should.js“等待”了一个异步函数

时间:2018-11-28 23:04:26

标签: javascript node.js sinon should.js

我有一个异步函数f,它调用了另一个异步函数g。为了测试f是否调用g,我正在使用sinon对g进行存根,并断言它是使用should.js调用的。

'use strict';

require('should-sinon');
const sinon = require('sinon');

class X {
    async f(n) {
        await this.g(n);
        // this.g(n); // I forget to insert `await`!
    }
    async g(n) {
        // Do something asynchronously
    }
}

describe('f', () => {
    it('should call g', async () => {
        const x = new X();
        sinon.stub(x, 'g').resolves();
        await x.f(10);
        x.g.should.be.calledWith(10);
    });
});

但是,即使我在await中调用g时忘记使用f,此测试仍通过。

捕获此错误的方法之一是使存根返回伪承诺,并检查其then是否被调用。

it('should call g', async () => {
    const x = new X();
    const dummyPromise = {
        then: sinon.stub().yields()
    };
    sinon.stub(x, 'g').returns(dummyPromise);
    await x.f(10);
    x.g.should.be.calledWith(10);
    dummyPromise.then.should.be.called();
});

但这有点麻烦。有什么方便的方法吗?

2 个答案:

答案 0 :(得分:0)

您为f设计的示例显示了有缺陷的代码设计,如果您使用async/await语法编写相同的函数,则代码设计将变得更加明显:

f(n) { return g(n).then(()=>{}); }

这实现了相同的行为-是否解决g变得很难说(假设您不知道f是否返回了g的承诺,这与不知道是一样的f是否等待g)。如果fg的结果不感兴趣,则应仅返回它,而不是将其隐藏。然后,您可以简单地测试结果。

如果您的意思是f可能必须依次触发多个async调用await,并调用多个g_1g_2 ...来解决,那么您可以通过在g_n+1的存根中断言g_n的伪承诺已经建立来构建测试链。通常,测试虚拟承诺的状态的方法很好。

答案 1 :(得分:0)

最好不要对then进行存根处理,而要在下一事件循环迭代中设置一些布尔值。然后,您可以在调用g后检查此布尔值,以确保f等待它:

f

编辑:当然,这不是完美保证,因为您可以让it('should call g', async () => { const x = new X(); let gFinished = false; sinon.stub(x, 'g').callsFake(() => { return new Promise((resolve) => { setImmediate(() => { gFinished = true; resolve(); }); }); }); await x.f(10); x.g.should.be.calledWith(10); gFinished.should.be.true(); }); 等待任何承诺,等待的时间至少要长于f解决。像这样:

g

这将导致我编写的测试通过,即使它仍然不正确。因此,实际上,这取决于您对测试的严格程度。您是否希望误报从字面上看是不可能的?还是可以通过一些明显的诡计将其抛弃?

在大多数情况下,我发现后者是可以的,但实际上这取决于您和/或您的团队。