【问题标题】:Testing Express routes with Sinon stubs使用 Sinon 存根测试 Express 路由
【发布时间】:2017-04-08 09:38:06
【问题描述】:

我正在尝试对 Express 路由进行单元测试,以验证它们调用了正确的控制器函数。此时,我不检查控制器功能的内部逻辑,只是检查路由映射到正确的功能。到目前为止,在我引入路由器中间件之前,我所拥有的一切都很好:

'use strict';

import express from 'express';
import config from './../../config/environments';
import SuperLogin from 'superlogin';

let controller = require('./subjects.controller');
let router = express.Router();
let superlogin = new SuperLogin(config);

router.get('/', superlogin.requireAuth, superlogin.requireAnyRole(['admin', 'specialist', 'nurse']), controller.index);
router.get('/:subjectId', superlogin.requireAuth, superlogin.requireAnyRole(['admin', 'specialist', 'nurse']), controller.show);
router.post('/', superlogin.requireAuth, superlogin.requireAnyRole(['admin', 'specialist', 'nurse']), controller.create);
router.put('/:subjectId', superlogin.requireAuth, superlogin.requireAnyRole(['admin', 'specialist', 'nurse']), controller.upsert);
router.patch('/:subjectId', superlogin.requireAuth, superlogin.requireAnyRole(['admin', 'specialist', 'nurse']), controller.patch);
router.delete('/:subjectId', superlogin.requireAuth, superlogin.requireAnyRole(['admin']), controller.destroy);


module.exports = router;

如您所见,我正在使用Superlogin 来验证对每条路线的访问权限。如果我删除这个我的测试通过,添加中间件会导致它们失败。我可能需要存根超级登录方法,但我不知道在哪里执行此操作。

我的测试如下

'use strict';

/* globals sinon, describe, expect, it */

let proxyquire = require('proxyquire').noPreserveCache();

let subjectCtrlStub = {
    index: 'subjectCtrl.index',
    show: 'subjectCtrl.show',
    create: 'subjectCtrl.create',
    upsert: 'subjectCtrl.upsert',
    patch: 'subjectCtrl.patch',
    destroy: 'subjectCtrl.destroy'
};

sinon.stub(superlogin, 'requireAuth', function(req, res, next) {
    return next();
});

let routerStub = {
    get: sinon.spy(),
    put: sinon.spy(),
    patch: sinon.spy(),
    post: sinon.spy(),
    delete: sinon.spy()
};

// require the index with our stubbed out modules
let subjectRoutes = proxyquire('./subjects.routes.js', {
    express: {
        Router() {
            return routerStub;
        }
    },
    './subjects.controller': subjectCtrlStub
});

describe('Subject API Router:', function() {

    it('should return an express router instance', function() {
        subjectRoutes.should.equal(routerStub);
    });

    describe('GET /api/subjects', function() {
        it('should route to subjects.controller.index', function() {
            routerStub.get
                .withArgs('/', 'subjectCtrl.index')
                .should.have.been.calledOnce;
        });
    });

    describe('GET /api/subjects/:subjectId', function() {
        it('should route to subjects.controller.show', function() {
            routerStub.get
                .withArgs('/:subjectId', 'subjectCtrl.show')
                .should.have.been.calledOnce;
        });
    });

    describe('POST /api/subjects', function() {
        it('should route to subjects.controller.create', function() {
            routerStub.post
                .withArgs('/', 'subjectCtrl.create')
                .should.have.been.calledOnce;
        });
    });

    describe('PUT /api/subjects/:subjectId', function() {
        it('should route to subjects.controller.upsert', function() {
            routerStub.put
                .withArgs('/:subjectId', 'subjectCtrl.upsert')
                .should.have.been.calledOnce;
        });
    });

    describe('PATCH /api/subjects/:subjectId', function() {
        it('should route to subjects.controller.patch', function() {
            routerStub.patch
                .withArgs('/:subjectId', 'subjectCtrl.patch')
                .should.have.been.calledOnce;
        });
    });

    describe('DELETE /api/subjects/:subjectId', function() {
        it('should route to subjects.controller.destroy', function() {
            routerStub.delete
                .withArgs('/:subjectId', 'subjectCtrl.destroy')
                .should.have.been.calledOnce;
        });
    });
});

我有一个 before 函数,它在所有这些测试之前运行,它使用一些具有不同角色的示例用户为数据库播种,并将授权令牌保存到全局变量中以在测试端点时使用,我只是不知道如何使用 routerStub 函数调用发送它们。

【问题讨论】:

    标签: node.js express mocha.js sinon


    【解决方案1】:

    使用stubs 提供具有已知值的SUT 的直接或间接输入

    您可能会考虑验证一个事实,即在某些情况下,应该从路由中访问哪些视图功能。在这种情况下,使用存根为中间件提供影响其行为的输入数据(例如用于身份验证的用户凭据)。不要用存根代替中间件本身,因为如果存根完全改变了行为,这样的测试就无关紧要了。

    【讨论】:

    • 你有什么例子说明我在使用 routerStub.get 方法时如何传递凭据? Superlogin 使用基于令牌的身份验证来决定访问控制,但由于我没有使用 HTTP 请求进行测试,因此无法添加授权标头。
    • 使用伪造的 HTTP 请求进行路由测试是个好主意。因此,测试可以针对不同类型的用户(匿名、使用各种角色进行身份验证)覆盖不同的真实场景。为什么不试试supertest-as-promised
    猜你喜欢
    • 2021-10-30
    • 2021-12-10
    • 2019-08-25
    • 2020-06-22
    • 2019-02-21
    • 2020-08-13
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多