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 설정 및 상호작용)
유지 보수 용이성 종합 ✅ 높음 (구조가 짧고 명확함) ⚠️ 중간 이하 (동등 멤버 정의) ❗ 가장 어려움 (테스트 대역 설정)