【问题标题】:How to mock request and response in nodejs to test middleware/controllers?如何在 nodejs 中模拟请求和响应以测试中间件/控制器?
【发布时间】:2013-01-23 19:31:00
【问题描述】:

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

所以我的问题是:如何在不启动服务器并向本地主机发送“真实”请求的情况下测试我的控制器。我想做的是像nodejs一样创建请求、响应实例,然后调用控制器方法。

类似这样的:

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

非常感谢任何建议。谢谢!

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

【问题讨论】:

  • 检查我刚刚制作的一个新模块:test-controller,用于模拟和执行基于 dupertest 的请求

标签: node.js testing express integration-testing mocha.js


【解决方案1】:

node-mocks-http 有一个半体面的实现

需要它:

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

然后您可以组合 req 和 response 对象:

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

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

var demoController = require('demoController');
demoController.login(req, res);

assert.equal(res.json, {})

警告

在编写此实现时存在与未触发事件发射器有关的问题。

【讨论】:

    【解决方案2】:

    由于 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)
    

    【讨论】:

    • 好吧,我明白这一点,但是我需要为 req.body、req.param 等编写大量代码。所以创建一个真实的实例会容易得多请求对象并重写一些方法
    • 也许可以试试sinon
    【解决方案3】:

    我会尝试为此使用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 变得超级容易,但默认情况下它们是非常简单的。

    【讨论】:

    • +1 我正在使用一个封装 dupertest 并创建更健壮的 req 和 res 对象的模块。我喜欢你对 dupertest 所做的事情(:
    • 如承诺的那样,here is test-controller 用于测试控制器
    【解决方案4】:

    如果你想使用真正的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);
        })
      })
    })
    

    【讨论】:

    • 快速提问 - 这是 Jasmine 测试,还是 Node 中包含的其他东西?我正在寻找自己的解决方案。谢谢!
    • 那是超级测试github.com/visionmedia/supertest。此设置的一个关键部分是您需要将应用程序的创建与服务器的运行分开。 Supertest 将在必要时负责启动服务器并发出请求。
    【解决方案5】:

    有点旧的帖子,但我想给我的 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....
    })
    

    【讨论】:

      【解决方案6】:

      看看node-tdduseNock 标志。它建立在 mocha 和 nock 之上,并为每个测试自动创建和使用记录文件。

      我们喜欢它的易用性。基本上只是“启用并忘记”并专注于编写请求/测试用例。如果要求更改测试,仍然需要删除或调整录制文件,但至少与代码完全分离。

      【讨论】:

        猜你喜欢
        • 2022-01-04
        • 2017-04-15
        • 1970-01-01
        • 2013-03-23
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多