1. 테스트 코드
코드의 신뢰성 확보와 예상치 못한 버그를 미리 찾아내기 위해 테스트 코드 작성은 중요하다.
테스트를 거치치 않은 코드는 기존 기능에 까지 영향을 미칠 수 있다.
2. 테스트 종류
a. 단위 테스트 (Unit Test) : 가장 작은 단위(함수, 컴포넌트 등)에 대한 테스트
- 일반적인 테스트, 세분화 테스트, 중요도가 높은 테스트
- 코드를 수정하거나 기능을 추가할 때, 수시로 빠른 검증이 가능하다.
- 리팩토링 시에 안정성을 확보할 수 있다.
- 개발 및 테스팅에 대한 시간 비용을 절감할 수 있다.
b. 통합 테스트 (Integratin Test) : 테스트를 모아서 진행하는 통합 테스트
c. E2E 테스트 (End to End Test) : 애플리케이션의 전체 워크플로우 테스트
- (로그인 후 특정 페이지로의 이동과 같은) 흐름을 테스트
d. 부하 테스트
3. 좋은 테스트의 특징
F. 빠를 것
I. 독립적일 것 : 각 테스트가 다른 테스트에 의존적이지 않고 독립적일 것
R. 반복 가능할 것 : 어떤 상황에서도 반복 가능할 것
S. 자체적으로 검증 될 것 : Bool값으로 결과를 내어 자체적으로 검증 될 것
T. 적시에 구현할 것 : 실제코드 작성 직전에 구현할 것
4. Jest
과거 js코드 테스트를 위해서는 여러 테스팅 라이브러리를 조합해서 사용해야 했었다.
세가지 종류의 라이브러리를 조합해야 했었다.
a. Test Runner : Mocha 또는 Jasmin
b. Test Mathcher : Chai 또는 Expect
c. Test Mock : Sinon 또는 Test double
이러한 불편한 상황을 개선하기 위해 메타는 Jest를 개발했다.
Jest의 등장으로 더이상 여러개의 라이브러리를 조합하여 테스트 코드를 작성할 필요가 없어졌다.
Jest는 Test Runner와 Test Mathcher 그리고 Test Mock 프레임워크까지 제공해준다.
즉, All-in-one 테스팅 프레임워크이다.
5. Jest 세팅
a. 설치 : npm i -D jest (TS일 경우 @types/jest 추가 설치 및 추가 설정 필요)
b. 스크립트 수정 : "test" : "jest"
c. test 파일 생성 : 테스트할함수파일명.test.js (== 테스트할함수파일명.spec.js)
d. test 파일 작성
e. 테스트 실행 : npm run test (=== npm test (like npm start))
6. Jest 작성법
// controllers/user.js
const User = require('../models/user');
exports.follow = async (req, res, next) => {
try {
const user = await User.findOne({ where: { id: req.user.id } });
if (user) {
await user.addFollowing(parseInt(req.params.id, 10));
res.send('success');
} else {
res.status(404).send('no user');
}
} catch (error) {
console.error(error);
next(error);
}
};
// controllers/user.test.js
jest.mock('../models/user');
const User = require('../models/user');
const { follow } = require('./user');
describe('follow', () => {
const req = {
user: { id: 1 },
params: { id: 2 },
};
const res = {
status: jest.fn(() => res),
send: jest.fn(),
};
const next = jest.fn();
test('사용자를 찾아 팔로잉을 추가하고 success를 응답해야 함', async () => {
User.findOne.mockReturnValue({
addFollowing(id) {
return Promise.resolve(true);
}
});
await follow(req, res, next);
expect(res.send).toBeCalledWith('success');
});
test('사용자를 못 찾으면 res.status(404).send(no user)를 호출함', async () => {
User.findOne.mockReturnValue(null);
await follow(req, res, next);
expect(res.status).toBeCalledWith(404);
expect(res.send).toBeCalledWith('no user');
});
test('DB에서 에러가 발생하면 next(error) 호출함', async () => {
const message = 'DB에러';
User.findOne.mockReturnValue(Promise.reject(message));
await follow(req, res, next);
expect(next).toBeCalledWith(message);
});
});
describe : 테스트를 그룹화한다. (첫번째 인수는 설명)
test, it : 테스트 케이스를 생성한다. (첫번째 인수는 설명)
expect : 함수의 결괏값이나 반환값을 지정한다
matcher : 비교할 값을 지정한다.
많이 쓰이는 4가지 matcher()
- toBe() : 엄격한 동등성 비교 (===)
- toEqual() : 단순 값 비교
- toBeCalled() : 함수 사용 여부 확인
- toBeCalledWith(x) : 인자값으로 x 사용 여부 확인
미들웨어랑 느낌 비슷한,
중복코드 제거를 위한 함수 4가지
- afterAll(()=>{}) : 실행 후 한번만
- afterEach(()=>{}) : 매 케이스 실행 후
- beforeAll(()=>{}) : 실행 전 한번만
- beforeEach(()=>{}) : 매 케이스 실행 전
6-1. Mock 함수
단위 테스트를 작성할 때,
외부 시스템이나 복잡한 의존성 없이 특정 함수나 메서드의 동작을 테스트하기 위해,
코드 일부를 가짜(mock)로 바꾸는 기법이다.
jest의 jest.fn() 메서드를 통해서 Mock 함수를 생성할 수 있다.
mockReturnValue를 통해서 Mock 함수의 반환값을 설정할 수 있다.
const mockFunction = jest.fn().mockReturnValue(42);
const result = mockFunction();
console.log(result); // 42
7. 테스트 커버리지
전체 코드 중 어떤 부분이 테스트 되었고, 어떤 부분이 테스트 되지 않았는지
비율을 알려주는 기능이다. (실행되는 라인 기준)
설정 : script 수정 "coverage" : "jest --coverage"
실행 : npm run coverage
퍼센트가 높다는 것은 많은 코드가 테스트 되었음을 의미한다.
하지만 맹목적으로 수치를 올리는 것보다, 의미있는 테스트 작성에 더 집중해야 한다.
8. 통합 테스트
DB의 처리로직을 포함한 API의 모든 트랜젝션 과정이 제대로 수행되었는지 확인하기 위한 테스트이다.
통합 테스트를 위해서 supertest 라이브러리를 사용해보자
supertest는 내부적으로 익스프레스 서버를 구동시켜 가상 요청을 보낸 뒤 결과 받아 검증하는 방식이다.
8-1. supertest 주요 메서드
request() : 가상 서버를 실행하고 API를 요청
agent() : 가상의 사용자를 두어 실제 서비스를 사용하듯 요청 상태 유지 (like 쿠키 가진 사용자)
8-2. supertest 사용법
a. 설치 : npm i supertest
b. app.js에서 listen분리 : app.js에서 listen을 새로 생성한 server.js로 이동
c. package.json 수정 : app.js > server.js
d. 테스트용 DB 설정 : config.json에서 test의 password와 database수정 (새로운 테스트용 database 생성)
e. 테스트용 DB 생성 : npx sequelize db:create --env test
f. 테스트 작성, 실행
9. 부하 테스트
서버가 얼마나 버티나 확인하기 위한 테스트
간단하게 artillery 라이브러리를 통해서 부하테스트를 해보자
(artillery는 포병대를 의미한다)
도스 공격과 마찬가지이므로 실제 서버에는 하지말자 (남 서버에 하면 범죄)
9-1. artillery 사용법
a. 설치 : npm i -D artillery
b. 서버 켜기
c. 폭격 : npm artillery quick --count 100 -n 50 http://localhost:8001
(http://localhost:8001로 100명이 50번씩 폭격)
디테일한 폭격을 위해 시나리오 작성도 가능하다.
(loadtest.json파일 생성 후 작성)
9-2. artillery 결과 해석
결과 평가는 주로 median과 p95를 기준으로 한다.
두 수치 자체가 너무 크지않고, 두 수치의 차이가 너무 크지 않으면 괜찮다.
(median, p95 단위 : ms)
'Node.js' 카테고리의 다른 글
4. 웹 API 서버 [express] (0) | 2024.12.09 |
---|---|
3. passport [express] (1) | 2024.12.02 |
2. express (서버제작 level 2) [express] (0) | 2024.11.11 |
1. http (서버제작 level 1) [express] (1) | 2024.11.05 |
3. 노드 기본 [Node] (8) | 2024.11.05 |