一个单元如何用Express测试路线?

Jam*_*ers 90 node.js express

我正在学习Node.js并且一直在使用Express.真的很喜欢框架;但是,我无法弄清楚如何为路线编写单元/集成测试.

能够对简单模块进行单元测试很容易,并且已经使用Mocha进行了测试 ; 但是,我的单元测试使用Express失败,因为我传入的响应对象不保留值.

正在测试的路由功能(routes/index.js):

exports.index = function(req, res){
  res.render('index', { title: 'Express' })
};
Run Code Online (Sandbox Code Playgroud)

单元测试模块:

var should = require("should")
    , routes = require("../routes");

var request = {};
var response = {
    viewName: ""
    , data : {}
    , render: function(view, viewData) {
        viewName = view;
        data = viewData;
    }
};

describe("Routing", function(){
    describe("Default Route", function(){
        it("should provide the a title and the index view name", function(){
        routes.index(request, response);
        response.viewName.should.equal("index");
        });

    });
});
Run Code Online (Sandbox Code Playgroud)

当我运行它时,它失败了"错误:检测到全局泄漏:viewName,data".

  1. 我哪里出错,以便我可以让这个工作?

  2. 有没有更好的方法让我在这个级别对我的代码进行单元测试?

更新 1.更正了代码片段,因为我最初忘记了"it()".

Ric*_*aca 35

正如其他人在评论中所建议的那样,看起来测试Express控制器的规范方法是通过supertest.

示例测试可能如下所示:

describe('GET /users', function(){
  it('respond with json', function(done){
    request(app)
      .get('/users')
      .set('Accept', 'application/json')
      .expect(200)
      .end(function(err, res){
        if (err) return done(err);
        done()
      });
  })
});
Run Code Online (Sandbox Code Playgroud)

好处:你可以一次性测试你的整个堆栈.

缺点:感觉和行为有点像集成测试.

  • 我同意你的缺点,这不是单元测试.这依赖于所有单元的集成来测试应用程序的URL. (16认同)
  • 我认为说"路径"实际上是"整合"是合法的,也许测试路线应该留给集成测试.我的意思是,匹配到它们定义的回调的路由的功能可能已经由express.js测试; 获取路径最终结果的任何内部逻辑应理想地在其外部进行模块化,并且这些模块应进行单元测试.他们的互动,即路线,应该进行整合测试.你同意吗? (7认同)

Lin*_*iel 19

更改您的响应对象:

var response = {
    viewName: ""
    , data : {}
    , render: function(view, viewData) {
        this.viewName = view;
        this.data = viewData;
    }
};
Run Code Online (Sandbox Code Playgroud)

它会奏效.

  • 这是对请求处理程序进行单元测试,而不是路由。 (4认同)

Ray*_*nos 19

使用express测试HTTP的最简单方法是窃取TJ的http助手

亲自使用他的助手

it("should do something", function (done) {
    request(app())
    .get('/session/new')
    .expect('GET', done)
})
Run Code Online (Sandbox Code Playgroud)

如果你想专门测试你的路线对象,那么传入正确的模拟

describe("Default Route", function(){
    it("should provide the a title and the index view name", function(done){
        routes.index({}, {
            render: function (viewName) {
                viewName.should.equal("index")
                done()
            }
        })
    })
})
Run Code Online (Sandbox Code Playgroud)

  • 似乎更先进的HTTP单元测试方法是使用Visionmedia的[supertest](https://github.com/visionmedia/supertest).似乎TJ的http助手已经演变为超级. (16认同)
  • 遗憾的是,这是集成测试而不是单元测试. (9认同)
  • 你能解决'帮助'链接吗? (5认同)
  • 在github上的supertest可以找到[这里](https://github.com/visionmedia/supertest) (2认同)

Luk*_*e H 18

我得出结论,真正单元测试表达应用程序的唯一方法是在请求处理程序和核心逻辑之间保持很多分离.

因此,您的应用程序逻辑应该位于可以进行required和单元测试的单独模块中,并且对Express Express和Response类的依赖性最小.

然后在请求处理程序中,您需要调用核心逻辑类的适当方法.

一旦我完成了当前应用程序的重组,我就会举一个例子!

我觉得这样的事情?(随意分享要点或评论,我仍然在探索这个).

编辑

这是一个很小的例子,内联.有关更详细的示例,请参阅要点.

/// usercontroller.js
var UserController = {
   _database: null,
   setDatabase: function(db) { this._database = db; },

   findUserByEmail: function(email, callback) {
       this._database.collection('usercollection').findOne({ email: email }, callback);
   }
};

module.exports = UserController;

/// routes.js

/* GET user by email */
router.get('/:email', function(req, res) {
    var UserController = require('./usercontroller');
    UserController.setDB(databaseHandleFromSomewhere);
    UserController.findUserByEmail(req.params.email, function(err, result) {
        if (err) throw err;
        res.json(result);
    });
});
Run Code Online (Sandbox Code Playgroud)

  • 在我看来,这是最好的模式。许多跨语言的 Web 框架使用控制器模式将业务逻辑与实际的 http 响应形成功能分开。这样,你可以只测试逻辑而不是整个http响应过程,这是框架的开发人员应该自己测试的东西。可以在此模式中测试的其他内容是简单的中间件、一些验证功能和其他业务服务。数据库连接测试是一种完全不同类型的测试 (3认同)
  • 事实上,这里的很多答案实际上都与集成/功能测试有关。 (3认同)

Eri*_*ulz 7

如果单位测试用快递4注意这个例子来自gjohnson:

var express = require('express');
var request = require('supertest');
var app = express();
var router = express.Router();
router.get('/user', function(req, res){
  res.send(200, { name: 'tobi' });
});
app.use(router);
request(app)
  .get('/user')
  .expect('Content-Type', /json/)
  .expect('Content-Length', '15')
  .expect(200)
  .end(function(err, res){
    if (err) throw err;
  });
Run Code Online (Sandbox Code Playgroud)