38

我的应用程序有几个层次:中间件、控制器、管理器。控制器接口与中间件一相同:(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)

任何建议都将受到高度赞赏。谢谢!

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

4

6 回答 6

23

在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, {})

警告

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

于 2015-06-30T12:14:17.660 回答
11

由于 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)
于 2013-01-23T21:58:04.033 回答
5

我会尝试为此使用dupertest。这是我创建的一个节点模块,目的是为了方便控制​​器测试,而无需启动新服务器。

它保留了类似requestor之类的节点模块的熟悉语法supertest,但同样不需要启动服务器。

它运行起来很像上面提到的 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,但默认情况下它们非常裸露。

于 2014-06-15T07:07:19.753 回答
4

如果要使用真实reqres对象,则必须向服务器发送真实请求。然而,这比你想象的要容易得多。express 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);
    })
  })
})
于 2013-02-05T09:15:14.603 回答
4

有点旧的帖子,但我想给我的 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....
})
于 2015-01-26T14:57:15.493 回答
0

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

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

于 2019-10-07T14:24:22.293 回答