如何使用 mocha 测试我的 express 应用程序?

Posted

技术标签:

【中文标题】如何使用 mocha 测试我的 express 应用程序?【英文标题】:How do i test my express app with mocha? 【发布时间】:2012-02-08 13:25:26 【问题描述】:

我刚刚将 shouldjs 和 mocha 添加到我的 express 应用程序中进行测试,但我想知道如何测试我的应用程序。我想这样做:

app = require '../app'
routes = require '../src/routes'

describe 'routes', ->
  describe '#show_create_user_screen', ->
    it 'should be a function', ->
      routes.show_create_user_screen.should.be.a.function
    it 'should return something cool', ->
      routes.show_create_user_screen().should.be.an.object

当然,该测试套件中的最后一个测试只是告诉我 res.render 函数(在 show_create_user_screen 中调用)未定义,可能是因为服务器未运行且配置尚未完成。所以我想知道其他人是如何设置测试的?

【问题讨论】:

只是想补充一下,上面的例子是因为它简短而简洁。通常我会测试在调用我的一个路由器函数之后设置/调用给定 req/res 对象上的适当函数或值。下面的答案就足够了。不应该测试路由器的功能,这是网络框架的工作。 【参考方案1】:

好的,首先,虽然测试路由代码是您可能想做也可能不想做的事情,但一般来说,尝试将您感兴趣的业务逻辑分离为与 express 或任何框架分离的纯 javascript 代码(类或函数)正在使用和使用香草摩卡测试来测试它。一旦你实现了这一点,如果你想真正测试你在 mocha 中配置的路由,你需要将 mock req, res 参数传递到你的中间件函数中,以模仿 express/connect 和你的中间件之间的接口。

对于一个简单的案例,您可以创建一个模拟 res 对象,其中包含一个类似于下面的 render 函数。

describe 'routes', ->
  describe '#show_create_user_screen', ->
    it 'should be a function', ->
      routes.show_create_user_screen.should.be.a.function
    it 'should return something cool', ->
      mockReq = null
      mockRes =
        render: (viewName) ->
          viewName.should.exist
          viewName.should.match /createuser/

      routes.show_create_user_screen(mockReq, mockRes).should.be.an.object

FYI 中间件函数也不需要返回任何特定值,这是它们对 req, res, next 参数所做的事情,您应该在测试中关注这些参数。

这是您在 cmets 中请求的一些 JavaScript。

describe('routes', function() 
    describe('#show_create_user_screen', function() 
      it('should be a function', function() 
        routes.show_create_user_screen.should.be.a["function"];
      );
      it('should return something cool', function() 
        var mockReq = null;
        var mockRes = 
          render: function(viewName) 
            viewName.should.exist;
            viewName.should.match(/createuser/);
          
        ;
        routes.show_create_user_screen(mockReq, mockRes);
      );
    );
  );

【讨论】:

模拟没有给您的一件事是防止您正在使用的模块的 api 更改。例如如果 express 更新并更改了渲染的名称,您将不受保护。理想情况下,您也在测试它,但有时集成+单元测试可以一次测试大量代码,这取决于您如何看待它,这是好事还是坏事。编辑:虽然我真的很喜欢这种模拟方法,但它真的很轻量级。 能不能总是加编译好的js,有些人不熟悉coffeescript。 这不是测试实现细节吗?您实际上想测试“响应”对象在返回时包含的内容 - 如果将来您不使用“渲染”方法来执行此操作,例如在重构期间,您的测试将失败并且不会向您显示重构的代码有效,因为您必须重写测试?只是一个想法 !否则,这是一种模拟响应对象的聪明方法。 Supertest 是另一种进行更多端到端测试的方法。两者都有其用途。【参考方案2】:

你可以试试 SuperTest,然后服务器的启动和关闭都处理好了:

var request = require('supertest')
  , app     = require('./anExpressServer').app
  , assert  = require("assert");

describe('POST /', function()
  it('should fail bad img_uri', function(done)
    request(app)
        .post('/')
        .send(
            'img_uri' : 'foobar'
        )
        .expect(500)
        .end(function(err, res)
            done();
        )
  )
);

【讨论】:

SuperTest 为我工作了至少十几个项目。为了胜利! 不知道supertestchaihttp有什么区别?【参考方案3】:

在connect.js tests suites中找到了替代方案

他们使用supertest 来测试一个连接应用程序,而没有将服务器绑定到任何端口,也没有使用模型。

这是 connect 的静态中间件测试套件的摘录(使用 mocha 作为测试运行器,使用 supertest 作为断言)

var connect = require('connect');

var app = connect();
app.use(connect.static(staticDirPath));

describe('connect.static()', function()
  it('should serve static files', function(done)
    app.request()
    .get('/todo.txt')
    .expect('contents', done);
  )
);

这也适用于快递应用

【讨论】:

我只能接受一个答案,否则也会被接受 =) app.request 在最新的 express/connect 中对我不起作用,所以我更新了这个答案以匹配github.com/visionmedia/supertest的用法 关于supertest 的条款看起来具有误导性。 connect code 中似乎没有提到它。无论如何,Alexandru 的答案看起来比其他人更好。 这里是使用supertest的连接部分:github.com/senchalabs/connect/blob/…【参考方案4】:

我发现最简单的方法是设置一个 TestServer 类用作帮助器,以及一个 帮助 http 客户端,并且只是向真正的 http 服务器发出真正的请求。不过,在某些情况下,您可能想要模拟和存根这些东西。

// Test file
var http = require('the/below/code');

describe('my_controller', function() 
    var server;

    before(function() 
        var router = require('path/to/some/router');
        server = http.server.create(router);
        server.start();
    );

    after(function() 
        server.stop();
    );

    describe("GET /foo", function() 
        it('returns something', function(done) 
            http.client.get('/foo', function(err, res) 
                // assertions
                done();
            );
        );
    );
);


// Test helper file
var express    = require('express');
var http       = require('http');

// These could be args passed into TestServer, or settings from somewhere.
var TEST_HOST  = 'localhost';
var TEST_PORT  = 9876;

function TestServer(args) 
    var self = this;
    var express = require('express');
    self.router = args.router;
    self.server = express.createServer();
    self.server.use(express.bodyParser());
    self.server.use(self.router);


TestServer.prototype.start = function() 
    var self = this;
    if (self.server) 
        self.server.listen(TEST_PORT, TEST_HOST);
     else 
        throw new Error('Server not found');
    
;

TestServer.prototype.stop = function() 
    var self = this;
    self.server.close();
;

// you would likely want this in another file, and include similar 
// functions for post, put, delete, etc.
function http_get(host, port, url, cb) 
    var options = 
        host: host,
        port: port,
        path: url,
        method: 'GET'
    ;
    var ret = false;
    var req = http.request(options, function(res) 
        var buffer = '';
        res.on('data', function(data) 
            buffer += data;
        );
        res.on('end',function()
            cb(null,buffer);
        );
    );
    req.end();
    req.on('error', function(e) 
        if (!ret) 
            cb(e, null);
        
    );


var client = 
    get: function(url, cb) 
        http_get(TEST_HOST, TEST_PORT, url, cb);
    
;

var http = 
    server: 
        create: function(router) 
            return new TestServer(router: router);
        
    ,

    client: client
;
module.exports = http;

【讨论】:

刚刚意识到我错过了你的问题的重点,但也许这无论如何都会有所帮助。我个人不会自己测试路由器功能。我只是通过 HTTP 请求测试服务器基本上完成了它应该做的事情,然后分别测试所有业务逻辑,因为它都在控制器之外的文件中。 您有对 path/to/some/router 的引用,查看该文件的内容会很有帮助。【参考方案5】:

mocha 带有用于 bdd 测试的 before、beforeEach、after 和 afterEach。在这种情况下,您应该在 describe 调用中使用 before。

describe 'routes' ->
  before (done) ->
    app.listen(3000)
    app.on('connection', done)

【讨论】:

以上是关于如何使用 mocha 测试我的 express 应用程序?的主要内容,如果未能解决你的问题,请参考以下文章

使用 Express、Socket.io 和 Node-Telegram-Bot-Api 结束 Mocha 测试

单元/集成测试 Express REST API, mongoose, mocha, sinon, chai, supertest

在 mocha 中,如何在执行另一个测试之前等待异步测试结束?

使用 mocha 测试 express 服务器并使用异步初始化程序进行 supertest 调用请求两次

Mocha + TypeScript:不能在模块外使用导入语句

Mocha 忽略了一些测试,尽管它们应该运行