E2E 테스트 구현
E2E(End-to-End) 테스트는 애플리케이션의 전체 흐름을 사용자 관점에서 검증하는 테스트 방식입니다.
단위 테스트가 개별 컴포넌트를, 통합 테스트가 컴포넌트 간 상호작용을 검증한다면 E2E 테스트는 실제 사용 시나리오를 모방하여 시스템 전체의 동작을 확인합니다.
E2E 테스트의 중요성
- 사용자 경험 검증 : 실제 사용자 시나리오를 테스트
- 시스템 통합 확인 : 모든 컴포넌트가 함께 올바르게 작동하는지 검증
- 회귀 테스트 : 새로운 기능 추가 시 기존 기능의 정상 작동 확인
NestJS E2E 테스트 환경 설정
- 의존성 설치
npm install --save-dev @nestjs/testing supertest
- Jest 설정
{
"moduleFileExtensions": ["js", "json", "ts"],
"rootDir": ".",
"testEnvironment": "node",
"testRegex": ".e2e-spec.ts$",
"transform": {
"^.+\\.(t|j)s$": "ts-jest"
}
}
- E2E 테스트 파일 생성
import { Test, TestingModule } from '@nestjs/testing';
import { INestApplication } from '@nestjs/common';
import * as request from 'supertest';
import { AppModule } from './../src/app.module';
describe('AppController (e2e)', () => {
let app: INestApplication;
beforeEach(async () => {
const moduleFixture: TestingModule = await Test.createTestingModule({
imports: [AppModule],
}).compile();
app = moduleFixture.createNestApplication();
await app.init();
});
it('/ (GET)', () => {
return request(app.getHttpServer())
.get('/')
.expect(200)
.expect('Hello World!');
});
afterAll(async () => {
await app.close();
});
});
HTTP 요청 시뮬레이션과 응답 검증
Supertest를 사용한 GET 요청 테스트
it('/users (GET)', () => {
return request(app.getHttpServer())
.get('/users')
.expect(200)
.expect('Content-Type', /json/)
.expect(res => {
expect(Array.isArray(res.body)).toBeTruthy();
expect(res.body.length).toBeGreaterThan(0);
});
});
POST 요청 테스트
it('/users (POST)', () => {
return request(app.getHttpServer())
.post('/users')
.send({ name: 'John Doe', email: '[email protected]' })
.expect(201)
.expect(res => {
expect(res.body.id).toBeDefined();
expect(res.body.name).toBe('John Doe');
});
});
데이터베이스 연동 E2E 테스트
테스트 데이터베이스 설정
import { TypeOrmModule } from '@nestjs/typeorm';
const moduleFixture: TestingModule = await Test.createTestingModule({
imports: [
TypeOrmModule.forRoot({
type: 'sqlite',
database: ':memory:',
entities: [/* 엔티티 목록 */],
synchronize: true,
}),
AppModule,
],
}).compile();
테스트 데이터 관리
import { getRepository } from 'typeorm';
import { User } from './user.entity';
beforeEach(async () => {
const userRepository = getRepository(User);
await userRepository.clear(); // 테스트 전 데이터 초기화
await userRepository.save([
{ name: 'Test User 1', email: '[email protected]' },
{ name: 'Test User 2', email: '[email protected]' },
]);
});
인증이 필요한 엔드포인트 테스트
JWT 인증 테스트
let authToken: string;
beforeAll(async () => {
const response = await request(app.getHttpServer())
.post('/auth/login')
.send({ username: 'testuser', password: 'testpass' });
authToken = response.body.token;
});
it('/protected (GET)', () => {
return request(app.getHttpServer())
.get('/protected')
.set('Authorization', `Bearer ${authToken}`)
.expect(200);
});
비동기 작업 테스트
긴 실행 시간을 가진 프로세스 테스트
it('should handle long running process', async () => {
const response = await request(app.getHttpServer())
.post('/long-process')
.timeout(10000) // 타임아웃 설정
.expect(202);
expect(response.body.jobId).toBeDefined();
// 작업 완료 확인
await new Promise(resolve => setTimeout(resolve, 5000));
const result = await request(app.getHttpServer())
.get(`/long-process/${response.body.jobId}`)
.expect(200);
expect(result.body.status).toBe('completed');
});
마이크로서비스 아키텍처 E2E 테스트
마이크로서비스 모킹
import { ClientsModule, Transport } from '@nestjs/microservices';
const moduleFixture: TestingModule = await Test.createTestingModule({
imports: [
ClientsModule.register([
{ name: 'USER_SERVICE', transport: Transport.TCP },
]),
AppModule,
],
}).compile();
app.connectMicroservice({
transport: Transport.TCP,
options: { port: 3001 },
});
await app.startAllMicroservices();
성능 및 부하 테스트 통합
Apache JMeter 또는 k6와 같은 도구를 사용한 부하 테스트
import * as k6 from 'k6';
import http from 'k6/http';
export default function() {
http.get('http://localhost:3000/users');
}
export let options = {
vus: 10,
duration: '30s',
};
E2E 테스트 최적화
- 병렬 실행 : Jest의
--maxWorkers
옵션 사용 - 데이터베이스 트랜잭션 : 각 테스트 후 롤백
- 테스트 격리 : Docker 컨테이너 사용
Best Practices
- 실제 환경과 유사한 테스트 환경 구성
- 중요한 사용자 흐름에 집중
- 데이터 설정과 정리를 철저히 관리
- 안정적인 테스트를 위해 적절한 대기 시간과 재시도 로직 구현
- CI/CD 파이프라인에 E2E 테스트 통합
- 성능 문제 감지를 위한 응답 시간 측정
- 테스트 결과 보고서 자동화
- 정기적인 테스트 스위트 리뷰 및 업데이트
주의사항
- E2E 테스트는 단위 테스트보다 실행 시간이 길 수 있음
- 외부 서비스 의존성 관리에 주의
- 테스트 데이터의 일관성 유지
- 비결정적 요소(타임스탬프, 랜덤 값 등) 처리에 주의
- 테스트 환경과 실제 환경의 차이 최소화
NestJS에서의 E2E 테스트 구현은 애플리케이션의 전체적인 동작을 검증하는 중요한 과정입니다.
Supertest와 NestJS의 테스팅 모듈을 활용하여 HTTP 요청을 시뮬레이션하고 응답을 검증할 수 있습니다.