jest를 통한 테스트 주도 개발

0

jest 초기 셋팅

npm install --save-dev jest 
npm install --save-dev @types/jest

npm install --save-dev supertest
npm install --save-dev @types/supertest

npm install --save-dev ts-jest

먼저 Jest 를 사용하기 위한 라이브러리들을 설치해준다.


import { Config } from '@jest/types';

const config: Config.InitialOptions = {
	preset: 'ts-jest', // 이 부분에서 ts-jest를 사용한다고 알려준다
	testEnvironment: 'node', // 테스트 환경 'node' 환경을 사용한다 알려준다.
	testMatch: ['**/test/*.test.(ts|tsx)'], // js 파일은 dist에서도 감지가 될 수 있으니 폴더를 조정해서 test이 있는 위치로 잡아준다
};

export default config;

그리고 해당 내용을 jest.config.ts에 작성해준다.


test코드 작성

beforeAll, afterAll로 DB 연결 및 해제

beforeAll(async () => {
	await mongo.connect();
});

afterAll(async () => {
	await mongoose.connection.dropDatabase();
	await mongoose.connection.close();
	await redis.disconnect();
});

beforeAll 함수는 jest 명령어에 의해 테스트가 시작될때 한번 실행되고, afterAll 명령어는 테스트가 종료될때 한번 메소드를 호출하게 된다.

통합 테스트 하나에는 단위 테스트가 여러개 있으며 단위 테스트를 한번 실행할때마다 DB와 연결하고 끊는 행위는 비효율적이다. 따라서 보통 통합 테스트 시작 전에 연결한 후, 통합 테스트가 끝나면 마지막에 연결을 끊는다. 이때 사용되는 함수가 Jest모듈의 beforeAll()afterAll() 함수이다.


beforeAll(async () => {
	const mongoUri = `${process.env.TEST_DATABASE_URL}`;

	const connectOption = {
		user: process.env.TEST_DATABASE_USER,
		pass: process.env.TEST_DATABASE_PASSWORD,
		dbName: process.env.TEST_DATABASE_NAME,
		heartbeatFrequencyMS: 2000,
	};

	await mongoose.connect(mongoUri, connectOption);
});

afterAll(async () => {
	await mongoose.connection.dropDatabase();
	await mongoose.connection.close();
	await redis.disconnect();
});

mocking을 하여 데이터베이스가 있는 것 처럼 테스트 하는 방법이 있지만, 나는 실제 test-db를 임시로 만들어 mongoDB에 테스트 시작 전에 연결한 다음, 테스트가 종료된후 dropDatabase()로 해당 db를 없애주었다.


request함수로 api test

test('[POST] /v1/auth/local/sign-in', async () => {
	await request(app)
		.post('/v1/auth/local/sign-in')
		.send({
			email: 'test@naver.com',
			password: 'pass1',
		})
		.expect({ data: true });
});

supertest 라이브러리의 request 함수는 가상의 서버를 실행하고 api를 요청한다.

해당 함수로 api요청을 하고 expect 함수로 결과값을 예측하여 맞는지 확인해주었다.


describe로 controller 구분

describe('AUTH API TEST', () => { 
	test('[POST] /v1/auth/local/sign-in', async () => {
		await request(app)
			.post('/v1/auth/local/sign-in')
			.send({
				email: 'test@naver.com',
				password: 'pass1',
			})
			.expect({ data: true });
	});
});
describe('STORE API TEST', () => { ... });
describe('COURSE API TEST', () => { ... });
...

그리고 API들을 한번에 작성하면 무슨 api인지 한눈에 알아보기 어렵기 때문에, describe 함수로 나누어서 가독성이 좋게 해주었다.


"test": "NODE_ENV=test jest --setupFiles dotenv/config --forceExit --detectOpenHandles",

그 다음 package.json 에 test 명령어 스크립트를 작성하여 해당 스크립트로 테스트를 진행해주었다.

jest --setupFiles dotenv/config: 이 부분은 jest 명령을 실행하며, dotenv/config를 사용하여 .env 파일을 로드한다.

-forceExit: 이 플래그는 Jest 테스트가 완료된 후에도 프로세스를 강제로 종료하도록 하는 옵션이다. 이렇게 하면 테스트가 완료된 후에 행렬을 정리하고 불필요한 리소스를 해제할 수 있다.

-detectOpenHandles: 이 플래그는 Jest가 비동기 작업 중에 열린 핸들(파일 디스크립터, 네트워크 소켓 등)을 감지하고, 테스트가 종료되지 않은 상태에서 열린 핸들을 보고하는 옵션이다. 이를 사용하여 비정상적인 핸들 누출을 감지하고 수정할 수 있다.


테스트가 완료되면 이렇게 나타난다.


husky를 통해 test 완료시 push

#!/usr/bin/env sh
. "$(dirname -- "$0")/_/husky.sh"

npm test

npm run build

코드를 수정하면 해당 api에 대한 응답이 변경될 수 있기 때문에, huskynpm test 명령어를 넣어주어서 push하기 전에 test를 실행하여 통과될 시에만 push가 가능하게 해주었다.


test가 실패하여 push가 실패한 경우

push가 되지 않는 것을 확인할 수 있다.


test가 성공하여 push가 성공한 경우

push가 성공적으로 완료된 것을 확인할 수 있다.

profile
https://www.youtube.com/watch?v=__9qLP846JE

0개의 댓글

관련 채용 정보