【问题标题】:How do i test my express app with mocha?如何使用 mocha 测试我的 express 应用程序?
【发布时间】: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 对象上的适当函数或值。下面的答案就足够了。不应该测试路由器的功能,这是网络框架的工作。

标签: node.js express mocha.js


【解决方案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)

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2013-01-23
    • 1970-01-01
    • 2018-08-05
    • 2017-02-17
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多