Ответ 1
Измените свой объект ответа:
var response = {
viewName: ""
, data : {}
, render: function(view, viewData) {
this.viewName = view;
this.data = viewData;
}
};
И он будет работать.
Я участвую в процессе обучения Node.js и играю с Express. На самом деле, как структура, однако, мне трудно понять, как написать тест unit/integration для маршрута.
Возможность unit test простых модулей легко и делала это с Mocha; однако мои модульные тесты с ошибкой Express, поскольку объект ответа, который я передаю, не сохраняет значения.
Функция маршрута в тесте (routes/index.js):
exports.index = function(req, res){
res.render('index', { title: 'Express' })
};
Unit Test Модуль:
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");
});
});
});
Когда я запускаю это, он терпит неудачу для "Ошибка: обнаружены глобальные утечки: имя_поиска, данные".
Где я ошибаюсь, чтобы я мог работать?
Есть ли лучший способ для меня unit test моего кода на этом уровне?
Обновление 1. Исправленный фрагмент кода, поскольку я изначально забыл "it()".
Измените свой объект ответа:
var response = {
viewName: ""
, data : {}
, render: function(view, viewData) {
this.viewName = view;
this.data = viewData;
}
};
И он будет работать.
Как другие рекомендации в комментариях, похоже, канонический способ тестирования 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()
});
})
});
Потенциал: вы можете протестировать весь свой стек за один раз.
Даунсайд: он чувствует и действует как тестирование интеграции.
Самый простой способ протестировать HTTP с помощью экспресс - это украсть TJ http helper
I лично использовать своего помощника
it("should do something", function (done) {
request(app())
.get('/session/new')
.expect('GET', done)
})
Если вы хотите специально протестировать свой объект маршрута, то выполните правильные макеты
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()
}
})
})
})
Я пришел к выводу, что единственный способ действительно unit test выразить приложения - поддерживать много разделения между обработчиками запросов и вашей основной логикой.
Таким образом, ваша логика приложения должна быть в отдельных модулях, которые могут быть require
d и проверены модулем, и имеют минимальную зависимость от классов Express Request и Response как таковых.
Затем в обработчиках запросов вам нужно вызвать соответствующие методы ваших основных логических классов.
Я приведу пример, когда закончу реструктуризацию моего текущего приложения!
Я думаю, что-то вроде this? (Не стесняйтесь разветвлять суть или комментарий, я все еще изучаю это).
Edit
Вот крошечный пример, inline. Подробнее см. gist.
/// 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);
});
});
если модульное тестирование с выражением 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;
});
Мне тоже было интересно, но специально для модульных тестов, а не для интеграционных тестов. Это то, что я делаю прямо сейчас,
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);
});
Где routerObj просто {router: expressRouter, path: '/api'}
.
Затем я загружаю в подпрограммы с помощью var loginRouterInfo = require('./login')(express.Router({mergeParams: true}));
, а затем экспресс-приложение вызывает функцию init, принимающую экспресс-маршрутизатор в качестве параметра. Затем initRouter вызывает router.use(loginRouterInfo.path, loginRouterInfo.router);
для монтирования подпрограммы.
Подпроцессор может быть протестирован с помощью:
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, '/');
});
Чтобы выполнить модульное тестирование вместо тестирования интеграции, я высмеивал объект ответа обработчика запросов.
/* 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);
});
});
Затем, чтобы достичь интеграционного тестирования, вы можете высмеять свой endpointHandler и вызвать конечную точку с помощью supertest.