💡 Jest로 node.js 어플리케이션 테스트하기
module_name.test.js
로 작성한다.1. Jest 설치
npm i jest --dev
2. package.json의 script에 추가
3. 테스트 실행
npm run test
runInBand
: 여러개의 테스트 파일이 오버랩되지 않고 순서대로 테스트하는 옵션(테스트 파일이 동시에 db에 접근하면 충돌이 날 수 있음)// package.json
{
"scripts": {
"test": "env-cmd -f ./config/test.env jest --watch runInBand"
},
"jest": {
"testEnvironment": "node"
},
//...
}
mongodb
의 url이 포함되어있다면, 해당 파일을 복사한뒤 test.env
와 같이 생성한다.mongodb
url이 연결된 원래의 환경변수 파일(dev.env
)을 그대로 사용한다면, 테스트시 사용되는 더미데이터가 계속 쌓일테니, 파일을 분리하는 것이다.dev.env
: MONGODB_URL=mongodb://localhost/task-managertest.env
: MONGODB_URL=mongodb://localhost/task-manager-testpackage.json
의 테스트 스크립트에 해당 경로를 추가한다. (env-cmd ./config/test.env
)expect
// math.js
const caculateTip = (total, tipPercent = 0.25) => total + total * tipPercent;
// math.test.js
test('Should calculate total with tip', () => {
const total = caculateTip(10, 0.3);
expect(total).toBe(13);
});
promise
/ async/await
// math.js
const addAsync = (a, b) => {
return new Promise((resolve, reject) => {
setTimeout(() => {
if (a < 0 || b < 0) {
return reject('Numbers must be non-negative');
}
resolve(a + b);
}, 2000);
});
};
// math.test.js
test('Should add two numbers with promise', done => {
addAsync(1, 3).then(sum => {
expect(sum).toBe(4);
done(); // (*)
});
});
test('Should add two numbers with async/await', async () => {
const sum = await addAsync(1, 3);
expect(sum).toBe(4);
});
npm i supertest --save-dev
supertest
라이브러리는 서버가 돌아가지 않아도 테스트가 가능하다.app
에 접근하기 위해서 index.js
와 app.js
로 분기routers
(e.g. user)와 동일하게 테스트 파일 생성// index.js
const app = require('./app');
app.listen(process.env.PORT, () => {
console.log('Server is up on ' + process.env.PORT);
});
// app.js
const express = require('express');
require('./db/mongoose');
const userRouter = require('./routers/user');
const taskRouter = require('./routers/task');
const app = express();
app.use(express.json());
app.use(userRouter);
app.use(taskRouter);
module.exports = app;
// tests/user.test.js
const request = require('supertest');
const app = require('../src/app');
// mockup data
const userOneId = new mongoose.Types.ObjectId();
const userOne = {
_id: userOneId,
name: 'Hanna',
email: 'hanna@gmail.com',
password: 'hanna1234',
tokens: [
{
token: jwt.sign({ _id: userOneId }, process.env.JWT_SECRET),
},
],
};
test('Should signup a new user', async () => {
await request(app)
.post('/users')
.send({
name: 'Suyeon',
email: 'suyeonme@gmail.com',
password: 'suyeonme1234',
})
.expect(201);
});
test('Should get profile for user', async () => { // Authentication
await request(app)
.get('/users/me')
.set('Authorization', `Bearer ${userOne.tokens[0].token}`) // 유효한 토큰일 경우
.send()
.expect(200);
});
아래의 경우, 테스트마다 동일한 이메일이 db에 저장되어 테스트가 실패한다. (모델에서 이메일을 유니크한 값으로 지정해놨기 때문에) 따라서 Jest에서 제공하는 글로벌 함수인, beforeEach를 사용하여 데이터베이스를 클리어한다.
const request = require('supertest');
const app = require('../src/app');
const User = require('../src/models/user');
// 안의 비동기 함수가 끝나면 테스트를 실행한다.
beforeEach(async () => {
await User.deleteMany();
});
test('Should signup a new user', async () => {
await request(app)
.post('/users')
.send({
name: 'Suyeon',
email: 'suyeonme@gmail.com',
password: 'suyeonme1234',
})
.expect(201);
});
response.body를 이용해서 테스트를 진행할 수 있다. 아래의 경우 새로 생성한 유저가 db에 잘 저장되어있는지 테스트하는 코드이다.
test('Should signup a new user', async () => {
const res = await request(app)
.post('/users')
.send({
name: 'Suyeon',
email: 'suyeonme@gmail.com',
password: 'suyeonme1234',
})
.expect(201);
// Assert that the db was changed correctly
const user = await User.findById(res.body.user._id);
expect(user).not.toBeNull();
// Assrtions about the response
expect(res.body).toMatchObject({
user: {
name: 'Suyeon',
email: 'suyeonme@gmail.com',
},
token: user.tokens[0].token,
});
});
test/fixtures
폴더 생성후, 테스트할 이미지를 넣는다.toBe
는 ===
를 사용하고, toEqual
은 ==
를 사용한다. 따라서 오브젝트를 비교할 때는 toEqual을 사용해야한다(e.g.{} === {}
false).test('Should upload avatar image', async () => {
await request(app)
.post('/users/me/avatar')
.set('Authorization', `Bearer ${userOne.tokens[0].token}`)
.attach('avatar', 'tests/fixtures/test-image.png')
.expect(200);
const user = await User.findById(userOneId);
expect(user.avatar).toEqual(expect.any(Buffer)); // (*)
});