본문으로 건너뛰기

테스트 가이드라인

1. 테스트 종류

1.1 단위 테스트

  • 모듈의 개별 컴포넌트 테스트
  • Jest 프레임워크 사용
  • 모든 public 메서드 테스트 필수
  • 의존성 모킹 필수

1.2 통합 테스트

  • 모듈 간 상호작용 테스트
  • 실제 데이터베이스 사용
  • API 엔드포인트 테스트
  • E2E 테스트 포함

2. 테스트 구조

2.1 파일 구조

module/
└── tests/
├── unit/
│ ├── controllers/
│ ├── services/
│ └── repositories/
└── integration/
├── api/
└── database/

2.2 네이밍 규칙

  • 단위 테스트: *.spec.ts
  • 통합 테스트: *.e2e-spec.ts
  • 테스트 데이터: *.fixture.ts

3. 테스트 작성 가이드라인

3.1 단위 테스트 예시

describe('UserService', () => {
let service: UserService;
let repository: MockType<Repository<User>>;

beforeEach(async () => {
const module = await Test.createTestingModule({
providers: [
UserService,
{
provide: getRepositoryToken(User),
useFactory: repositoryMockFactory,
},
],
}).compile();

service = module.get(UserService);
repository = module.get(getRepositoryToken(User));
});

it('should find a user by id', async () => {
const user = { id: 1, name: 'Test User' };
repository.findOne.mockReturnValue(user);

expect(await service.findOne(1)).toEqual(user);
expect(repository.findOne).toHaveBeenCalledWith({ where: { id: 1 } });
});
});

3.2 통합 테스트 예시

describe('User API (e2e)', () => {
let app: INestApplication;

beforeAll(async () => {
const moduleFixture = await Test.createTestingModule({
imports: [AppModule],
}).compile();

app = moduleFixture.createNestApplication();
await app.init();
});

it('/v1/users (GET)', () => {
return request(app.getHttpServer())
.get('/v1/users')
.expect(200)
.expect('Content-Type', /json/);
});

afterAll(async () => {
await app.close();
});
});

4. 테스트 데이터 관리

4.1 테스트 픽스처

export const userFixture = {
validUser: {
id: 1,
email: 'test@example.com',
name: 'Test User',
},
invalidUser: {
id: 2,
email: 'invalid-email',
name: '',
},
};

4.2 데이터베이스 시딩

  • 테스트 데이터베이스 사용
  • 매 테스트 전 데이터 초기화
  • 트랜잭션 롤백 활용

5. 모킹 가이드라인

5.1 모킹 대상

  • 외부 API 호출
  • 데이터베이스 연산
  • 이메일 서비스
  • 캐시 서비스

5.2 모킹 예시

const mockEmailService = {
sendEmail: jest.fn().mockResolvedValue(true),
};

const mockCacheService = {
get: jest.fn(),
set: jest.fn(),
del: jest.fn(),
};

6. 테스트 커버리지

6.1 커버리지 요구사항

  • 전체 코드 커버리지: 80% 이상
  • 모듈별 커버리지: 85% 이상
  • 핵심 비즈니스 로직: 90% 이상

6.2 커버리지 측정

npm run test:cov

7. 테스트 자동화

7.1 CI/CD 파이프라인

  • 커밋 시 단위 테스트 실행
  • PR 시 전체 테스트 실행
  • 배포 전 E2E 테스트 실행

7.2 테스트 환경

  • 개발 환경
  • 스테이징 환경
  • 프로덕션 환경

8. 성능 테스트

8.1 부하 테스트

  • 동시 사용자 처리
  • 응답 시간 측정
  • 리소스 사용량 모니터링

8.2 스트레스 테스트

  • 최대 부하 테스트
  • 장애 복구 테스트
  • 메모리 누수 테스트

9. 테스트 우선순위 및 범위

9.1 테스트 우선순위

  1. 핵심 비즈니스 로직
    • 도메인 서비스
    • 애그리게이트
    • 값 객체
  2. 응용 서비스
    • 커맨드 핸들러
    • 쿼리 핸들러
  3. 인프라스트럭처
    • 리포지토리
    • 외부 서비스 어댑터
  4. 인터페이스
    • API 컨트롤러
    • 미들웨어

9.2 도메인 테스트

  • 도메인 규칙 검증
  • 불변식 테스트
  • 상태 변이 테스트
  • 도메인 이벤트 테스트

9.3 경계 조건 테스트

  • null/undefined 처리
  • 빈 컬렉션
  • 최대/최소값
  • 특수 문자
  • 날짜/시간 경계

10. 테스트 품질

10.1 테스트 가독성

  • 명확한 테스트 설명
  • Given-When-Then 패턴
  • 의미 있는 변수명
  • 중복 코드 제거

10.2 테스트 유지보수성

  • 테스트 헬퍼 함수 활용
  • 공통 설정 분리
  • 테스트 데이터 중앙화
  • 불필요한 의존성 제거

10.3 안티패턴 방지

  • 너무 많은 모킹
  • 불필요한 검증
  • 깨지기 쉬운 테스트
  • 테스트 간 의존성

11. 보안 테스트

11.1 인증/인가 테스트

describe('AuthGuard', () => {
it('should block unauthorized access', async () => {
const response = await request(app.getHttpServer())
.get('/protected-resource')
.expect(401);
});

it('should allow access with valid token', async () => {
const token = await generateValidToken();
const response = await request(app.getHttpServer())
.get('/protected-resource')
.set('Authorization', `Bearer ${token}`)
.expect(200);
});
});

11.2 입력 검증 테스트

describe('UserInput', () => {
it('should validate email format', async () => {
const invalidEmail = 'invalid-email';
const response = await request(app.getHttpServer())
.post('/users')
.send({ email: invalidEmail })
.expect(400);
});

it('should prevent XSS attacks', async () => {
const maliciousInput = '<script>alert("xss")</script>';
const response = await request(app.getHttpServer())
.post('/users')
.send({ name: maliciousInput })
.expect(400);
});
});

12. 비동기 테스트

12.1 이벤트 핸들링

describe('UserEventHandler', () => {
it('should handle user created event', async () => {
// Given
const event = new UserCreatedEvent({ id: 1, email: 'test@example.com' });

// When
await eventHandler.handleUserCreated(event);

// Then
expect(emailService.sendWelcomeEmail).toHaveBeenCalledWith('test@example.com');
});
});

12.2 타임아웃 처리

describe('LongRunningOperation', () => {
it('should complete within timeout', async () => {
const operation = new LongRunningOperation();

await expect(operation.execute()).resolves.toEqual(
expect.anything()
).rejects.toThrow('Operation timed out');
}, 5000); // 5초 타임아웃
});

변경 이력

버전날짜작성자변경 내용
0.1.02025-03-16bok@weltcorp.com최초 작성
0.2.02025-03-26bok@weltcorp.com테스트 우선순위, 품질, 보안, 비동기 테스트 섹션 추가