使用 Promises 和 Spies 进行单元测试

Unit Testing with Promises and Spies

我有以下文件:-

target.js

var target = function(repository, logger) {

    return {
        addTarget : function(target) {
            repository.add(target).then(
                function (newTarget) {
                    console.log("added");
                    logger.info("added");
                },
                function (err) {
                    console.log("error");
                    logger.info("error");
                }
            );
        }
    };
};

module.exports = target;

targetTest.js

var chai = require("chai"),
    expect = chai.expect,
    sinon = require("sinon"),
    Promise = require("bluebird"),
    baseTarget = require("../target");

describe("target", function(){
    it("should log error when it occurs", function() {
        var mockRepository = {
            add : sinon.stub().returns(Promise.reject(new Error()))
        };

        var mockLogger = {
            info : sinon.spy()
        };

        var target = baseTarget(mockRepository, mockLogger);

        target.addTarget("target");

        expect(mockLogger.info.calledWith("error")).to.be.true;
    });
});

我遇到的问题是 expect(mockLogger.info.calledWith("error")).to.be.true; returns false 因为存储库上的 add 方法是异步的,因此尚未执行。是否有正确执行此操作的模式。

这实际上是一个关于 'how Promises work' 的问题,而不是关于它们如何在测试框架内工作的问题 - 答案是它们的行为保持完全相同。

Is there a pattern for doing this properly.

与其说它是一种模式,不如说它是构建 Promises 的目的。 then 的每个成功处理程序在最后一个成功时按顺序执行。在您的代码中,我们可以 return 通过调用 repository#add 创建的 Promise,就像您想要使用它的结果或在 addTarget 之外执行一些外部依赖操作一样:

addTarget: function (target) {
    return repository
//  ^^^^^^
        .add(target)
        .then(function (newTarget) {
            console.log("added");
            logger.info("added");
        }, function (err) {
            console.log("error");
            logger.info("error");
        });
}

然后将您的期望放在 then 中,它将在 addTarget:

中创建的 Promise 链的所有成员成功时执行
target.addTarget("target").then(function () {
    expect(mockLogger.info.calledWith("error")).to.be.true;
    cb();
});

异步测试

您会注意到在上面的示例中还有一个函数调用 cb。由于您的测试是异步的,您需要在测试完成后 'tell' 测试框架。这通常是通过使用参数声明 it 函数来完成的,框架将从该参数推断测试是异步的并传入回调:

describe("target", function () {
    it("should log error when it occurs", function (cb) {
                                                // ^^^^
    });
});