하나의 단위 테스트는 Express로 어떻게 라우팅됩니까?
저는 Node.js를 배우는 과정에 있으며 Express를 가지고 놀았습니다 . 프레임 워크와 정말 비슷하지만 경로에 대한 단위 / 통합 테스트를 작성하는 방법을 알아내는 데 어려움이 있습니다.
간단한 모듈 단위 테스트를 할 수 있다는 것은 쉽고 Mocha 와 함께 해왔습니다 . 그러나 전달하는 응답 객체가 값을 유지하지 않기 때문에 Express를 사용한 단위 테스트가 실패합니다.
테스트중인 경로 함수 (routes / index.js) :
exports.index = function(req, res){
res.render('index', { title: 'Express' })
};
단위 테스트 모듈 :
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");
});
});
});
이것을 실행하면 "오류 : 전역 누출 감지 : viewName, 데이터"에 대해 실패합니다.
이 작업을 수행 할 수 있도록 내가 어디로 잘못 가고 있습니까?
이 수준에서 내 코드를 단위 테스트하는 더 좋은 방법이 있습니까?
업데이트 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()
});
})
});
장점 : 전체 스택을 한 번에 테스트 할 수 있습니다.
단점 : 통합 테스트처럼 느껴지고 작동합니다.
Express로 HTTP를 테스트하는 가장 쉬운 방법은 TJ의 http 도우미 를 훔치는 것입니다.
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()
}
})
})
})
실제로 단위 테스트 익스프레스 애플리케이션을 수행하는 유일한 방법은 요청 핸들러와 핵심 로직 사이에 많은 분리를 유지하는 것이라는 결론에 도달했습니다.
따라서 응용 프로그램 논리는 require
d 및 단위 테스트 가 가능한 별도의 모듈에 있어야하며 Express Request 및 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);
});
});
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;
});
통합 테스트 대신 단위 테스트를 수행하기 위해 요청 처리기의 응답 개체를 조롱했습니다.
/* 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.
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.
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, '/');
});
참고URL : https://stackoverflow.com/questions/9517880/how-does-one-unit-test-routes-with-express
'developer tip' 카테고리의 다른 글
Swift에서 하나 이상의 프로토콜을 준수하는 특정 유형의 변수를 어떻게 선언 할 수 있습니까? (0) | 2020.09.06 |
---|---|
무엇이 유효하고 URI 쿼리에없는 것은 무엇입니까? (0) | 2020.09.06 |
TortoiseGit에서 "git did not exit cleanly (exit code 128)"오류를 해결하는 방법은 무엇입니까? (0) | 2020.09.06 |
다른 것 또는 프롬프트에서 Windows 배치 파일을 호출하는 여러 가지 방법. (0) | 2020.09.06 |
파이썬에서 좋은 기하학 라이브러리? (0) | 2020.09.06 |