首页 > 解决方案 > 存根没有在 .then() 内部被调用

问题描述

我正在对 Express 应用程序进行单元测试。我正在尝试模拟我的外部依赖项(Express、数据库等)并且接近突破。但是,我遇到了未从.then()我的业务逻辑内部调用存根的问题。

我尝试测试的几种方法如下:

/**
 * Ping
 * A simple endpoint to poke for testing.
 * @arg {*} request - Incoming request
 * @arg {*} response - Outgoing response
 * @arg {*} next - Next route in series
 */
ping: (request, response, next) => {
    let elapsed = Date.now() - request.start;
    response.status(200).json({
        request_started: new Date(request.start).toISOString(),
        request_duration: `${elapsed} milliseconds`
    });
},
/**
 * Registration
 * Allows for registration of a user name and password.
 * @arg {*} request - Incoming request
 * @arg {*} response - Outgoing response
 * @arg {*} next - Next route in series
 */
register: (request, response, next) => {
    let username = request.body.username;
    let password = request.body.password;
    this.model.registerUser(username, password).then(ret => {
        if (ret) {
            response.status(201).send("Created");
        } else {
            response.status(400).send("Error processing registration request. Please try again.");
        }
    }).catch(err => {
        response.status(400).send("Error processing registration request. Please try again.");
    });
}

中的模型register返回一个Promise包装对数据库的调用并根据结果进行回复的模型。我对此设置进行了模拟,如下所示:

mockModel: {
    registerUser: sinon.stub().callsFake(function(user, pass) {
        if (typeof user !== 'undefined') {
            return Promise.resolve(pass === 'pass');
        }
    }),
    getUser: sinon.stub().callsFake(function(user, pass) {
        if (typeof user !== 'undefined' && pass === 'pass') {
            return Promise.resolve({id: 9999});
        } else {
            return Promise.resolve(false);
        }
    })
}

我还模拟了该response对象,因此我可以将其传入并确定它是否被正确调用:

mockRes: () => {
    return {
        status: sinon.stub().returnsThis(),
        send: sinon.stub(),
        json: sinon.stub()
    };
}

当我进行测试时,问题就出现了:

describe('Register() method', function() {
    this.beforeEach(function() {
        req = mockReq(0, {}, {username: 'user', password: 'pass'});
        res = mockRes();
        base.BaseRoutes(router, null, base.methods, mockModel);
    });

    it('Returns a 201 Created message upon success', function() {
        base.methods.register(req, res);
        chai.expect(res.status).to.have.been.calledWith(201);
        chai.expect(res.send).to.have.been.calledWith('Created');
    });
});

此处的测试失败并出现以下错误:

1) Base Methods
       Register() method
         Returns a 201 Created message upon success:
     AssertionError: expected stub to have been called with arguments 201
      at Context.<anonymous> (test\spec.js:50:50)

使用调试器单步执行显示该方法正在被调用,但我遇到了这个失败。

同一套件中利用相同模拟请求/响应的其他测试正常工作,但它们不使用Promise调用(例如:ping() 方法)

我怀疑它与范围界定有关,但我不确定哪里出了问题。

标签: node.jsunit-testingmocha.jssinonsinon-chai

解决方案


又跑了几次,发现不是作用域的问题,而是异步的问题。测试用例在Promise解决/拒绝之前完成。

我缺少关闭循环的部分是我的 Express 路由处理程序需要返回Promise他们为处理数据库调用而创建的:

register: (request, response, next) => {
    let username = request.body.username;
    let password = request.body.password;
    return this.model.registerUser(username, password).then((ret) => {
        if (ret) {
            response.status(201).send("Created");
        } else {
            response.status(400).send("Error processing registration request. Please try again.");
        }
    }, (err) => {
            response.status(400).send("Error processing registration request. Please try again.");
    });
},

then()然后进行测试,在返回的 中执行我的断言Promise

it('Returns a 201 Created message upon success', function(done) {
    base.methods.register(req, res).then(x => {
        chai.expect(res.status).to.have.been.calledWith(201);
        chai.expect(res.send).to.have.been.calledWith('Created');  
    }).then(done, done);
});

it('Returns a 400 message upon failure', function(done) {
    req = mockReq(0, {}, {username: 'user', password: 'fail'});
    base.methods.register(req, res).then(x => {
        chai.expect(res.status).to.have.been.calledWith(400);
        chai.expect(res.send).to.have.been.calledWith(sinon.match.string);  
}).then(done, done);

虽然有传递Promise到测试并在那里处理它的示例以及单独测试 Express 路由处理程序的示例,但我找不到将两者结合并对其进行测试的示例。希望这可以为其他人服务。


推荐阅读