Jest로 Koa의 API를 테스트하기 위해서 겪었던 일들을 적기 위해 글을 썼다. 테스트 자동화에 가장 많이 쓰이는 도구 중 하나인 Mocha도 있지만 Jest가 설정이 간편하고 하나의 패키지에 모든게 들어있다고 하여 Jest를 사용하기로 했다.
(Jest는 mocha + should 의 조합이라고도 한다.)
yarn add --dev jest supertest
테스트는 개발할때만 사용할것이므로 개발 의존성으로 설치를 했다.
Jest는 테스트를 위한 라이브러리이고 Supertest는 API 테스트를 할 때 http 요청과 응답을 간편하게 만들어주는 라이브러리이다.
// jest.config.js
module.exports = {
// Mongoose는 jest의 default environment 을 제공하지 않으므로
// Node.js로 변경
testEnvironment: 'node',
// 모듈 require 경로
moduleDirectories: [
"node_modules",
"src"
]
};
맨처음엔 package.json 파일에 "jest" 필드를 넣어서 설정할 수 있다고 하길래 설정을 했다. 하지만 Mongoose를 사용하는 환경은 testEnvironment를 다르게 설정해줘야 한다고 알려주는 친절한 warning이 떠서 jest.config.js 파일에 작성하라길래 그냥 따라서 했다.(root 경로에 생성)
http://mongoosejs.com/docs/jest.html
moduleDirectories는 Jest 실행 시 외부와 내부모듈의 경로를 알려주는 설정이다. 프로젝트 내에서 모듈을 불러올 때 절대경로를 사용했기 때문에 이렇게 설정해주었다.
그다음엔 package.json 파일에 test 스크립트를 추가해주면 된다.
...
"scripts": {
"test": "jest"
},
...
이로써 터미널에 yarn test를 치면 테스트 코드가 돌아간다.
먼저 테스트를 하기 위해선 서버를 실행한 server 객체가 필요하다. 따라서 index.js 의 server 를 export 해주어야한다.
// src/index.js
const Koa = require('koa');
const app = new Koa();
...
// 중간 코드
...
// PORT 값이 설정되어있지 않다면 4000 을 사용합니다.
const port = process.env.PORT || 4000;
const server = app.listen(port, () => {
console.log('bgs server is listening to port ' + port);
});
module.exports = server;
auth.controller.js 중 회원가입 API는 다음과 같이 작성되어있다.
Method: POST
Endpoint: /api/auth/register/local
// src/api/auth/auth.controller.js
// 로컬 회원가입
exports.localRegister = async (ctx) => {
// 아이디 / 이메일 중복 체크
const existing = await Accounts.findByEmailOrUsername(ctx.request.body);
if (existing) {
// 중복되는 아이디/이메일이 있을 경우
ctx.status = 400;
// 어떤 값이 중복되었는지 알려줍니다
ctx.body = {
key: existing.email.address
=== ctx.request.body.email ? 'email' : 'username'
};
return;
}
// 생략
};
Jest는 .test.js로 작성된 파일이나 __test__폴더에 들어있는 test 파일들을 일괄적으로 테스트한다. 폴더와 파일을 생성하고 해당 API를 테스트하기 위해 중복되는 유저이름으로 회원가입을 했을 때 테스트코드를 작성해 보았다.
// test/auth.test.js
const request = require('supertest');
const server = require('index');
describe('Auth API local register 테스트', () => {
test('중복되는 아이디로 가입 시 409응답코드와 {key:username}객체 응답', async () => {
const response = await request(server)
.post('/api/auth/register/local')
.send({
username: 'googleUser',
email: 'hhsw1606@asdf.com',
password: '123123'
});
expect(response.status).toEqual(409);
expect(response.body).toEqual({
key: 'username'
});
});
});
test
로 테스트 하나를 작성할 수 있다. 테스트케이스에 해당한다
describe
로 여러 테스트케이스를 묶을 수 있다. 해당 블록 안에서는 스코프가 공유되므로 변수를 같이 사용할 수 있다. 또한 테스트 실행 시 테스트 메시지가 nesting되어 나타난다.
supertest의 request 함수를 사용해서 http요청을 보냈다. server가 구동된 뒤에 요청을 보내야 하므로 비동기식으로 코드를 작성해야한다.
자주 사용되는 Matcher 의 종류는 다음과 같다.
true
, false
인지 확인
테스트 결과 정상적으로 수행된 것을 확인했다. 하지만 테스트 실행이 완료되었음에도 종료가 되지 않는다. 노란 글씨로 문제점을 대충 짚어주는데 방법을 찾아서 적용해봐야겠다.