Code/Test
[단위 테스트] 6-1. 단위 테스트 스타일: 단위 테스트 스타일
noahkim_
2025. 1. 25. 00:20
블라디미르 코리코프 님의 "단위 테스트" 책을 정리한 포스팅입니다.
1. 단위 테스트의 세 가지 스타일
설명 | 출력 기반 테스트 | 상태 기반 테스트 | 통신 기반 테스트 |
검증 항목 | 함수의 반환 값만 검증 | 시스템의 상태나 의존성의 상태 변경 검증 |
협력자와의 상호작용, 통신 여부 검증
|
예시 | 함수형 프로그래밍 - 순수 함수 (로직, 부작용 ❌) |
도메인 모델 상태 변화 - 결제 후 주문 상태가 "완료"로 변경되었는지 검증 |
외부 시스템 호출
- 주문 서비스가 결제 서비스에 호출을 했는지 검증 - 호출된 파라미터가 맞는지 검증 |
예시) 출력 기반 테스트
더보기
더보기
public class Calculator {
public int add(int a, int b) {
return a + b;
}
}
public class CalculatorTest {
@Test
public void testAdd() {
Calculator calculator = new Calculator();
int result = calculator.add(2, 3);
assertEquals(5, result); // 입력 (2, 3) -> 출력 5
}
}
예시) 상태 기반 테스트
더보기
더보기
public class OrderService {
private Order order;
public void completeOrder(Order order) {
order.setStatus("완료");
}
public String getOrderStatus(Order order) {
return order.getStatus();
}
}
public class OrderServiceTest {
@Test
public void testCompleteOrder() {
Order order = new Order();
OrderService orderService = new OrderService();
orderService.completeOrder(order);
// 상태가 "완료"로 변경되었는지 확인
assertEquals("완료", orderService.getOrderStatus(order));
}
}
예시) 통신 기반 테스트
더보기
더보기
public class OrderService {
private PaymentService paymentService;
public OrderService(PaymentService paymentService) {
this.paymentService = paymentService;
}
public boolean processPayment(Order order) {
return paymentService.processPayment(order.getAmount());
}
}
public class OrderServiceTest {
@Test
public void testProcessPayment() {
PaymentService mockPaymentService = mock(PaymentService.class);
OrderService orderService = new OrderService(mockPaymentService);
Order order = new Order();
order.setAmount(100);
// 결제 서비스가 실제로 호출되었는지 확인
when(mockPaymentService.processPayment(100)).thenReturn(true);
boolean paymentStatus = orderService.processPayment(order);
assertTrue(paymentStatus);
verify(mockPaymentService).processPayment(100); // 결제 서비스가 호출되었는지 검증
}
}
2. 단위 테스트 스타일 비교
회귀 방지 vs 테스트 피드백 속도
항목 | 회귀 방지 | 테스트 피드백 속도 |
정의 | 변경 후에도 기존 기능이 올바르게 동작하는 것을 보장 |
테스트 실행 후 결과를 확인하기까지 걸리는 시간
|
주요 지표 | - 코드 복잡도 - 도메인 유의성 |
- 테스트 구조 (병렬성, 의존성 격리)
- 환경 구성 (DB, 네트워크) |
중요 요소 | 의미 있는 테스트 여부 |
실행 속도
|
핵심 | "무엇을 테스트하는가"가 중요 |
구조와 환경이 피드백 속도에 영향을 미침
|
리팩터링 내성 지표
- 거짓양성 수에 대한 척도 (실제로는 올바르게 동작하지만 테스트가 실패하는 현상)
- 캡슐화 위반이나 구현 세부사항 의존 때문에 발생
- 캡슐화를 잘 지키고 테스트를 식별할 수 있는 동작에만 결합하면 거짓 양성을 줄일 수 있음
테스트 스타일 | 거짓 양성 가능성 | 결합 대상 |
출력 기반 | ❌ 낮음 (우수) |
오직 입력과 출력 함수에만 결합
|
상태 기반 | ⚠️ 중간 |
메서드 + 클래스 내부 상태에 결합
|
통신 기반 | ❗ 높음 (취약) |
메서드 + 협력 객체와의 상호작용에 결합
|
유지 보수성 지표
지표 | 출력 기반 테스트 | 상태 기반 테스트 | 통신 기반 테스트 |
이해 난이도 | ✅ 낮음 (입력과 출력만 보면 됨) | ⚠️ 높음 (내부 상태 및 맥락 파악) | ⚠️ 높음 (상호작용 흐름 파악) |
실행 난이도 | ✅ 낮음 (간결하고 직접 실행 가능) | ⚠️ 중간 (다양한 상태 설정) | ❗ 높음 (mock 설정 및 상호작용) |
유지 보수 용이성 종합 | ✅ 높음 (구조가 짧고 명확함) | ⚠️ 중간 이하 (동등 멤버 정의) | ❗ 가장 어려움 (테스트 대역 설정) |