
이번 장은 NodeBird 서비스에 테스팅을 적용하는 장임.
개발자나 QA들이 실제 서비스를 개발 완료한 후, 자신이 만든 서비스가 제대로 동작하는지 테스트함.
(테스팅 중 테스트 과정을 자동화 하는 경우도 있음)
11.1 테스트 준비하기
$npm i -D jset
package.json에는 test라는 명령어를 등록해줌.
명령어를 실행할 때 jest가 실행 됨.
//package.json
{
"name":...
"version":...
...
"scrpits":{
"start":...
"test":"jest"
},
...
}
이 후 폴더 안에 middlewares.test.js를 만든다.
테스트용 파일은 파일명과 확장자 사이에 test나 spec을 넣으면 된다.
테스트 예시)
기대한 값과 실제 결과가 정확히 일치할 때의 모습이다.
test('문자열 합치기 테스트', () => {
const hello = "Hello";
const world = "World";
// "Hello"와 "World"를 더하면 "HelloWorld"가 될 것이라고 기대함
expect(hello + world).toEqual("HelloWorld");
});
실행결과:
PASS testing/middlewares.test.js
✓ 문자열 합치기 테스트 (1 ms)
Test Suites: 1 passed, 1 total
Tests: 1 passed, 1 total
...
기대한 값과 실제 결과가 다를 때 나타나는 모습이다. Jest는 무엇이 틀렸는지 친절하게 알려준다.
test('일부러 틀려보는 테스트', () => {
const result = 10 + 20;
// 결과는 30이지만, 일부러 50이라고 기대값을 설정
expect(result).toEqual(50);
});
실행결과:
FAIL testing/middlewares.test.js
✕ 일부러 틀려보는 테스트 (5 ms)
● 일부러 틀려보는 테스트
expect(received).toEqual(expected) // deep equality
Expected: 50
Received: 30
15 |
16 | // 결과는 30이지만, 일부러 50이라고 기대값을 설정
> 17 | expect(result).toEqual(50);
| ^
11.2 유닛 테스트
//middlewares.test.js
const { isLoggedIn,isNotLoggedIn} = require("./middlewares");
describe('isLoggedIn',()=>{
const res={
status:jest.fn(()=>res),
send:jest.fn(),
};
const next = jest.fn();
test('로그인되어 있으면 isLoggedIn이 next를 호출해야 함',()=>{
const req={
isAuthenticated:jest.fn(()=>true),
};
isLoggedIn(req,res,next);
expect(next).toBeCalledTimes(1);
});
test('로그인되어 있지 않으면 isLoggedIn이 에러를 응답해야 함',()=>{
const req={
isAuthenticated:jest.fn(()=>false),
};
isLoggedIn(req,res,next);
expect(res.status).toBeCalledWith(403);
expect(res.send).toBeCalledWith('로그인 필요');
});
});
모킹(mocking): 가짜 객체, 가짜 함수를 넣는 행위
(원래 단어의 뜻도 속이다. 임)
우선 미들웨어가 사용하는 req, res, next 인자를 테스트 환경에서 구현하기 위해 Jest의 jest.fn() 메서드를 활용한다.
특히 res 객체의 경우, 실제 코드에서 res.status(403).send('...')와 같이 메서드를 이어서 사용하는 '메서드 체이닝'이 빈번하게 발생한다. 이를 재현하기 위해 res.status 가짜 함수가 실행된 후 다시 res 객체 자신을 반환하도록 jest.fn(() => res) 형태로 정의하는 것이 이 코드의 핵심이다.
이러한 모킹 방식은 외부 환경(DB, 세션)에 의존하지 않고 미들웨어 내부의 분기 로직만 독립적으로 검증할 수 있게 해준다.
미들웨어 테스트
미들웨어를 유닛 테스트하기 위해서는 라우터 핸들러 내부에 익명 함수로 작성하는 대신, 별도의 파일로 로직을 분리해야 한다. 그래야 테스트 코드에서 해당 함수를 직접 require하여 독립적으로 검증할 수 있기 때문이다.
미들웨어 분리 예시)
로그인 여부를 판단하는 두 가지 미들웨어를 별도의 모듈로 구성한 코드
// middlewares.js
// 로그인 상태를 확인하는 미들웨어
exports.isLoggedIn = (req, res, next) => {
if (req.isAuthenticated()) {
next(); // 로그인 되어 있으면 다음 미들웨어로
} else {
res.status(403).send('로그인 필요'); // 안 되어 있으면 에러 응답
}
};
// 로그인하지 않은 상태인지 확인하는 미들웨어
exports.isNotLoggedIn = (req, res, next) => {
if (!req.isAuthenticated()) {
next(); // 로그인 안 되어 있으면 다음으로
} else {
const message = encodeURIComponent('로그인한 상태입니다.');
res.redirect(`/?error=${message}`); // 로그인 되어 있으면 홈으로 리다이렉트
}
};
실제 라우터에서의 사용 방식)
분리된 미들웨어는 라우터에서 다음과 같이 삽입되어 동작함.
const express = require('express');
const router = express.Router();
const { isLoggedIn, isNotLoggedIn } = require('./middlewares');
// isLoggedIn 미들웨어가 먼저 실행된 후, 통과하면 다음 익명 함수가 실행됨
router.get('/profile', isLoggedIn, (req, res) => {
res.render('profile');
});
// isNotLoggedIn 미들웨어가 통과되어야 회원가입 페이지 접근 가능
router.get('/join', isNotLoggedIn, (req, res) => {
res.render('join');
});
11.3 테스트 커버리지
전체 코드에서 작동하는 부분을 확인하는 방법
{
...
"scripts": {
"start":"nodemon app",
"test": "jest",
"coverage":"jest --coverage"
},
}
jest 명령어 뒤에 --corverage 옵션을 붙여 jest가 테스트 커버리지를 분석하게 함.
$npm run coverage
테스트 결과가 출력되고, 추가적으로 표 하나가 더 출력 됨.
% Stmts
//구문 비율
% Branch
//if문 등의 분기점 비율
% Funcs
//함수 비율
% Lines
//코드 줄 수 비율
Uncovered Line #s
//커버되지 않은 줄 위치
(퍼센티지가 높을 수록 많은 코드가 테스트 되었다는 뜻)
11.4 통합 테스트
하나의 라우터에는 여러 개의 미들웨어가 붙어 있고, 다양한 라이브러리가 사용됨.
이런 것들이 모두 유기적으로 잘 작동하는지 테스트 하는 것
$npm i -D supertest
테스트를 위해 supertest를 설치함
- supertest를 사용하기 위해서는 app객체를 모듈로 만들어 분리해야 함
- app.js파일에서 app객체를 모듈로 만든 후, server.js에서 불러와 listen한다.
- server.js는 app의 포트 리스닝만 담당한다.
"scripts": {
"start":"nodemon server",
"test": "jest",
"coverage":"jest --coverage"
},
테스트용 데이터베이스 설정
//routes/auth.test.js
const request = require('supertest');
const { sequelize } = require('../models');
const app = require('../app');
beforeAll(async ()=>{
await sequilize.sync();
});
describe('POST /login',()=>{
test('로그인 수행', (done)=>{
request(app)
.post('/auth/login')
.send({
"email":"zerohch@gmail.com",
"password":"nodejsbook"
})
.expect('Location','/')
.expect(302, done);
});
});
Supertest를 이용해 실제 서버에 요청을 보내고 응답을 확인하는 통합 테스트
11.5 부하 테스트
서버가 얼마만큼의 요청을 견딜 수 있는지(또는 수용할 수 있는지) 테스트하는 방법
$npm i -D artillery
$npm start
$npx artillery quick --count 100 -n 50 http://localhost:8001
$npx artillery quick ... : localhost에 빠르게 부하 테스트를 하는 방법.
--count: 가상의 사용자 수
-n: 요청 횟수
실행결과:
--------------------------------------
Metrics report @ 2025-12-22T00:15:00Z
--------------------------------------
http.codes.200: ............................. 5000
http.request_rate: ........................... 240/sec
http.requests: ............................... 5000
.
.
.
Request lateny:
min: ....................................... 2
max: ....................................... 150
median: .................................... 12
p95: ....................................... 45
p99: ....................................... 92
Request latency: 응답 지연 속도
request latency가 중요한데, 대부분의 요청이 비슷한 속도로 처리되기 위해선 median과 p95값의 차이가 크지 않으면 좋다.
예시 코드)
//loadtest.json
{
"config":{
"target":"http://localhost:8001",
"phases":[
{
"duration":60,
"arrivalRate":30
}
]
},
"scenarios":[{
"flow":[
{
"get":{
"url":"/"
}
},{
"post":{
"url":"/auth/login",
"json":{
"email":"zerohch@gmail.com",
"password":"nodejsbook"
}
}
},{"get":{
"url":"/hashtag?hashtag=nodebird"
}
}
]
}]
}
- config 객체에서 target을 현재 서버로 함
- phase에서 60초 동안(duration)
- 매초 30명의 사용자(arrivalRate)를 생성함
테스트 실행시 모든 요청이 모든 데이터베이스에 최소 한 번씩 접근함으로 무리가 된다.
테스트를 진행할수록 요청의 속도가 느려지는 것은 서버가 부하 테스트를 하는 정도의 요청을 감당하지 못한다는 뜻이다.
해결 방안을 고민해야 한다.
해결방안 예시)
서버의 사양 업그레이드
서버를 여러 개 두기
코드를 더 효율적으로 개선
const res = {
status: ( A ),
send: jest.fn(),
};
출처 : 조현영, 『 Node.js 교과서 개정 3판』, 길벗(2022),
Corner Node.js 1
Editor : GYEOMGYOEM
| [Node.js 1팀] 10장. 웹 API 서버 만들기 (0) | 2025.12.19 |
|---|---|
| [Node.js 1팀] 9장. 익스프레스로 SNS 서비스 만들기 (1) | 2025.11.28 |
| [Node.js 1팀] 8장. 몽고디비 (0) | 2025.11.21 |
| [Node.js 1팀] 7장. MySQL (0) | 2025.11.14 |
| [Node.js 1팀] 5장. 패키지 매니저, 6장. 익스프레스 웹 서버 만들기 (0) | 2025.11.07 |