首页 文章

如何模拟nodejs中的请求和响应来测试中间件/控制器?

提问于
浏览
27

我的应用程序有几个层次:中间件,控制器,管理器 . 控制器接口与中间件一样:( req,res,next) .

所以我的问题是:如何在不启动服务器的情况下测试我的控制器并向localhost发送“真实”请求 . 我想要做的是创建请求,响应实例作为nodejs,然后只调用控制器方法 .

像这样的东西:

var req = new Request()
var res = new Response()
var next = function(err) {console.log('lala')}
controller.get_user(req, res, next)

任何建议都非常感谢 . 谢谢!

附:我想要这样做的原因是,最后我想测试响应对象是否包含玉视图的正确变量 .

5 回答

  • 4

    在node-mocks-http上有一个半好的实现

    需要它:

    var mocks = require('node-mocks-http');
    

    然后,您可以编写req和响应对象:

    req = mocks.createRequest();
    res = mocks.createResponse();
    

    然后,您可以直接测试您的控制器:

    var demoController = require('demoController');
    demoController.login(req, res);
    
    assert.equal(res.json, {})
    

    警告

    在此实现中编写问题时,事件 Launcher 未被触发 .

  • 6

    由于JavaScript是一种动态类型语言,因此您可以创建模拟对象并将它们传递给控制器,如下所示:

    var req = {};
    var res = {};
    var next = function(err) {console.log('lala')}
    controller.get_user(req, res, next)
    

    如果您的控制器需要您的请求或响应对象中的特定数据或功能,则需要在模拟中提供此类数据或功能 . 例如,

    var req = {};
    req.url = "http://google.com"; // fake the Url
    
    var res = {};
    res.write = function(chunk, encoding) { 
      // fake the write method 
    };
    
    var next = function(err) {console.log('lala')}
    controller.get_user(req, res, next)
    
  • 4

    我会尝试使用dupertest . 这是我创建的节点模块,其目的是简化控制器测试,而无需启动新服务器 .

    它保留了熟悉的节点模块语法,如 requestsupertest ,但同样不需要启动服务器 .

    它的运行方式与上面提到的Hector非常相似,但是与Jasmine等测试框架集成在一起,感觉更加无缝 .

    与您的问题相关的示例可能如下所示:

    request(controller.get_user)
      .params({id: user_id})
      .expect(user, done);
    

    或者更明确的速记版本:

    request(controller.get_user)
      .params({id: user_id})
      .end(function(response) {
        expect(response).toEqual(user);
        done();
      });
    

    注意:示例假设 user_iduser 在某处定义,并且控制器根据id抓取并返回用户 .

    编辑:阅读您对上述答案的回答,我将承认目前的缺点是该模块默认情况下不会集成更强大的模拟请求或响应对象 . dupertest 使扩展和添加属性非常容易 reqres ,但默认情况下它们非常简单 .

  • 9

    如果要使用真正的 reqres 对象,则必须向服务器发送实际请求 . 然而,这比你想象的要容易得多 . 快递github repo有很多例子 . 以下显示 req.route 的测试

    var express = require('../')
      , request = require('./support/http');
    
    describe('req', function(){
      describe('.route', function(){
        it('should be the executed Route', function(done){
          var app = express();
    
          app.get('/user/:id/edit', function(req, res){
    
            // test your controllers with req,res here (like below)
    
            req.route.method.should.equal('get');
            req.route.path.should.equal('/user/:id/edit');
            res.end();
          });
    
          request(app)
          .get('/user/12/edit')
          .expect(200, done);
        })
      })
    })
    
  • 14

    有点老帖,但我想给我2美分 . 您想要采取的方法取决于您是在进行单元测试还是集成测试 . 如果您正在使用supertest的路线,这意味着您正在运行实际的实现代码,这意味着您正在进行集成测试 . 如果那是你想要做的,这种方法很好 .

    但是如果你正在进行单元测试,你会模拟req和res对象(以及所涉及的任何其他依赖项) . 在下面的代码中(为了简洁而删除了不相关的代码),我正在模拟res并且只给出json方法的模拟实现,因为这是我测试所需的唯一方法 .

    // SUT
    kids.index = function (req, res) {
    if (!req.user || !req.user._id) {
        res.json({
            err: "Invalid request."
        });
    } else {
        // non-relevent code
    }
    };
    
    // Unit test
    var req, res, err, sentData;
    describe('index', function () {
    
        beforeEach(function () {
            res = {
                json: function (resp) {
                    err = resp.err;
                    sentData = resp.kids;
                }
            };
        });
        it("should return error if no user passed in request", function () {
            req = {};
    
            kidsController.index(req, res);
            expect(err).to.equal("Invalid request.");
    
        });
    
        /// More tests....
    })
    

相关问题