node.js 一个单元如何使用 Express 测试路由?

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/9517880/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-09-02 15:15:40  来源:igfitidea点击:

How does one unit test routes with Express?

node.jsexpress

提问by JamesEggers

I'm in the process of learning Node.js and have been playing around with Express. Really like the framework;however, I'm having trouble figuring out how to write a unit/integration test for a route.

我正在学习 Node.js 并且一直在玩Express。真的很喜欢这个框架;但是,我在弄清楚如何为路由编写单元/集成测试时遇到了麻烦。

Being able to unit test simple modules is easy and have been doing it with Mocha; however, my unit tests with Express fail since the response object I'm passing in doesn't retain the values.

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

Route-Function Under Test (routes/index.js):

被测路由功能(routes/index.js):

exports.index = function(req, res){
  res.render('index', { title: 'Express' })
};

Unit Test Module:

单元测试模块:

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");
        });

    });
});

When I run this, it fails for "Error: global leaks detected: viewName, data".

当我运行它时,它因“错误:检测到全局泄漏:viewName,数据”而失败。

  1. Where am I going wrong so that I can get this working?

  2. Is there a better way for me to unit test my code at this level?

  1. 我哪里出错了才能让它工作?

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

Update1. Corrected code snippet since I initially forgot "it()".

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

采纳答案by Linus Gustav Larsson Thiel

Change your response object:

更改您的响应对象:

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

And it will work.

它会起作用。

回答by Rich Apodaca

As others have recommended in comments, it looks like the canonical way to test Express controllers is through supertest.

正如其他人在评论中所推荐的那样,测试 Express 控制器的规范方法似乎是通过supertest

An example test might look like this:

示例测试可能如下所示:

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()
      });
  })
});

Upside: you can test your entire stack in one go.

好处:您可以一次性测试整个堆栈。

Downside: it feels and acts a bit like integration testing.

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

回答by Luke H

I've come to the conclusion that the only way to really unit test express applications is to maintain a lot of separation between the request handlers and your core logic.

我得出的结论是,真正对 Express 应用程序进行单元测试的唯一方法是在请求处理程序和核心逻辑之间保持大量分离。

Thus, your application logic should be in separate modules that can be required and unit tested, and have minimal dependence on the Express Request and Response classes as such.

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

Then in the request handlers you need to call appropriate methods of your core logic classes.

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

I'll put an example up once I've finished restructuring my current app!

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

I guess something like this?(Feel free to fork the gist or comment, I'm still exploring this).

我猜是这样的(随意分叉要点或评论,我仍在探索这一点)。

Edit

编辑

Here's a tiny example, inline. See the gistfor a more detailed example.

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

/// 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);
    });
});

回答by Raynos

The easiest way to test HTTP with express is to steal TJ's http helper

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

I personally use his helper

个人使用他的帮手

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

If you want to specifically test your routes object, then pass in correct mocks

如果你想专门测试你的路由对象,然后传入正确的模拟

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()
            }
        })
    })
})

回答by ErichBSchulz

if unit testing with express 4 note this example from gjohnson :

如果使用 express 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;
  });

回答by fxlemire

To achieve unit testing instead of integration testing, I mocked the response object of the request handler.

为了实现单元测试而不是集成测试,我模拟了请求处理程序的响应对象。

/* app.js */
import endpointHandler from './endpointHandler';
// ...
app.post('/endpoint', endpointHandler);
// ...

/* endpointHandler.js */
const endpointHandler = (req, res) => {
  try {
    const { username, location } = req.body;

    if (!(username && location)) {
      throw ({ status: 400, message: 'Missing parameters' });
    }

    res.status(200).json({
      location,
      user,
      message: 'Thanks for sharing your location with me.',
    });
  } catch (error) {
    console.error(error);
    res.status(error.status).send(error.message);
  }
};

export default endpointHandler;

/* response.mock.js */
import { EventEmitter } from 'events';

class Response extends EventEmitter {
  private resStatus;

  json(response, status) {
    this.send(response, status);
  }

  send(response, status) {
    this.emit('response', {
      response,
      status: this.resStatus || status,
    });
  }

  status(status) {
    this.resStatus = status;
    return this;
  }
}

export default Response;

/* endpointHandler.test.js */
import Response from './response.mock';
import endpointHandler from './endpointHander';

describe('endpoint handler test suite', () => {
  it('should fail on empty body', (done) => {
    const res = new Response();

    res.on('response', (response) => {
      expect(response.status).toBe(400);
      done();
    });

    endpointHandler({ body: {} }, res);
  });
});

Then, to achieve integration testing, you can mock your endpointHandler and call the endpoint with supertest.

然后,为了实现集成测试,您可以模拟您的 endpointHandler 并使用supertest调用端点。

回答by Marcus R?dell

I was wondering this as well, but specifically for unit tests and not integration tests. This is what I'm doing right now,

我也想知道这一点,但专门用于单元测试而不是集成测试。这就是我现在正在做的

test('/api base path', function onTest(t) {
  t.plan(1);

  var path = routerObj.path;

  t.equals(path, '/api');
});


test('Subrouters loaded', function onTest(t) {
  t.plan(1);

  var router = routerObj.router;

  t.equals(router.stack.length, 5);
});

Where the routerObj is just {router: expressRouter, path: '/api'}. I then load in subrouters with var loginRouterInfo = require('./login')(express.Router({mergeParams: true}));and then the express app calls an init-function taking in the express router as a parameter. The initRouter then calls router.use(loginRouterInfo.path, loginRouterInfo.router);to mount the subrouter.

routerObj 只是{router: expressRouter, path: '/api'}. 然后我加载子路由器,var loginRouterInfo = require('./login')(express.Router({mergeParams: true}));然后 express 应用程序调用一个 init 函数,将 express 路由器作为参数。initRouter 然后调用router.use(loginRouterInfo.path, loginRouterInfo.router);挂载子路由器。

The subrouter can be tested with:

可以使用以下方法测试子路由器:

var test = require('tape');
var routerInit = require('../login');
var express = require('express');
var routerObj = routerInit(express.Router());

test('/login base path', function onTest(t) {
  t.plan(1);

  var path = routerObj.path;

  t.equals(path, '/login');
});


test('GET /', function onTest(t) {
  t.plan(2);

  var route = routerObj.router.stack[0].route;

  var routeGetMethod = route.methods.get;
  t.equals(routeGetMethod, true);

  var routePath = route.path;
  t.equals(routePath, '/');
});