블라디미르 코리코프 님의 "단위 테스트" 책을 정리한 포스팅입니다.
1. 통합 테스트는 무엇인가?
- 단위 테스트만으로 전체 시스템이 잘 동작하는지 확신할 수 없음
- 각 부분이 외부 시스템과 어떻게 통합되는지 확인해야 함
- 비즈니스 로직을 격리된 상태로 확인하는 것만으로는 충분하지 않음
통합 테스트
- 여러 컴포넌트가 실제처럼 함께 작동하는지 확인하는 테스트
- 외부 시스템과 통합된 상태에서 작동 여부를 검증함
구분 | 설명 |
대상 | 외부 시스템과 상호작용하는 코드 (컨트롤러, 애플리케이션 서비스 등) |
목적 |
시스템이 외부 의존성과 통합된 상태에서 올바르게 작동하는지 검증
|
검증 범위 |
단일 동작 단위를 검증 (예: 하나의 API 요청 처리 흐름)
|
속도 | 단위 테스트보다 느릴 수 있음 |
테스트 격리 |
테스트 환경을 격리하여 의도한 통합 동작만 검증
|
단위 테스트와 통합 테스트의 비율
- 단위 테스트로 가능한 한 많이 비즈니스 시나리오의 예외 상황을 확인
- 통합 테스트로 주요 흐름과 단위 테스트가 다루지 못하는 기타 예외 사항 다루기
빠른 실패
- 통합 테스트는 일반 흐름 + 예외 흐름을 테스트해야 함
- 하지만 앱이 아예 작동하지 못하게 만드는 극단적인 실패는 테스트 대상이 아님 (예: DB 접속 안 됨)
- 이런 극단적인 상황은 실행 즉시 드러나는 문제이므로, 따로 시나리오를 짤 필요가 없음
2. 어떤 프로세스 외부 의존성을 직접 테스트해야 하는가?
프로세스 외부 의존성의 두 가지 유형
구분 | 설명 | 외부에서 관찰 가능 여부 | 예시 |
관리 의존성 | 구현 세부 사항 (애플리케이션 내부에서만 접근) | ❌ |
내부 캐시, in-memory store
|
비관리 의존성 | 외부 시스템 (식별 가능한 부작용 발생) | ✅ |
SMTP 서버, 메시지 버스
|
통합 테스트에서 실제 데이터베이스를 사용할 수 없으면 어떻게 할까?
사용할 수 없는 경우
상황 | 설명 |
관리 범위를 벗어남 |
외부 팀 또는 다른 시스템에서 관리하여 테스트 환경 설정이 어려움
|
실제 버전을 사용할 수 없음 |
테스트에 맞는 버전의 DB를 사용할 수 없음 (예: 라이센스 제한, 레거시 시스템)
|
테스트 환경에 배포 불가 |
테스트 환경에 데이터베이스 인스턴스를 설치하거나 구성할 수 없음
|
대처 방법
전략 | 설명 |
✔ 통합 테스트 생략 |
DB가 핵심 요소지만 사용할 수 없다면, 통합 테스트는 포기하고 단위 테스트에 집중
|
✔ 도메인 모델 단위 테스트 집중 |
비즈니스 로직의 핵심을 도메인 모델에서 검증함으로써 품질 확보
|
✔ QA 단계에서 외부 통신 집중 검증 |
QA 환경에서는 실제 DB 및 외부 시스템을 통합하여 검증 가능
|
🚫 목(Mock) 사용 피하기 |
❌ 쿼리 동작 테스트 불가
❌ 리팩터링 내성 약화: 메서드 호출 여부만 검증, 내부 구현 바뀌면 테스트가 깨지거나 무의미해짐 |
3. CRM 시스템
시나리오
회사 이메일을 일반 이메일로 바꾸기
- 이메일이 변경됨
- 유저 타입이 Employee → Customer로 바뀜
- 회사 직원 수가 1 줄어듦
- 메시지 버스로 "이메일 변경됨" 메시지 전송
데이터베이스와 메시지 버스 분류하기
외부 의존성을 관리 / 비관리로 나누어 목 여부를 결정하기
- 애플리케이션 내부 데이터베이스: 관리 의존성 (구현 상세)
- 메시지 버스: 비관리 의존성 (다른 시스템과의 통신)
엔드 투 엔드 테스트는 어떤가?
외부 클라이언트를 모방
- 테스트 범위에 포함된 모든 프로세스 외부 의존성을 참조하는 배포된 버전의 애플리케이션을 테스트
- 실제 API를 사용하여 시나리오를 수행 (목으로 대체 X)
통합 테스트
예제
더보기
public void Changing_email_from_non_corporate_to_corporate() {
var db = new Database(ConnectionString);
var user = CreateUser("user@gmail.com", UserType.Employee, db);
CreateCompany("mycorp.com", 1, db);
var messageBusMock = new Mock<IMessageBus>();
var sut = new UserController(db, messageBusMock.Object);
string result = sut.ChangeEmail(user.UserId, "new@gmail.com");
Assert.Equal("OK", result);
object[] userData = db.GetUserById(user.UserId);
User userFromDb = UserFactory.Create(userData);
Assert.Equal("new@gmail.com", userFromDb.Email);
Assert.Equal(UserType.Customer, userFromDb.Type);
object[] companyData = db.GetCompany();
Company companyFromDb = CompanyFactory.Create(companyData);
Assert.Equal(0, companyFromDb.NumberOfEmployees);
messageBusMock
.Verify(x => x.SendEmailChangedMessage(user.UserId, "new@gmail.com"), Times.Once);
}
'Code > Test' 카테고리의 다른 글
[단위 테스트] 9. 목 처리에 대한 모범 사례 (0) | 2025.03.01 |
---|---|
[단위 테스트] 8-2. 통합 테스트를 하는 이유: 인터페이스 (1) | 2025.03.01 |
[단위 테스트] 7-2. 가치 있는 단위 테스트를 위한 리팩터링: 감사 시스템 (0) | 2025.02.28 |
[단위 테스트] 7-1. 가치 있는 단위 테스트를 위한 리팩터링: 코드 유형 (0) | 2025.02.27 |
[단위 테스트] 6-2. 단위 테스트 스타일: 함수형 아키텍처 (0) | 2025.01.29 |