Code/Test

[단위 테스트] 2-1. 단위 테스트란 무엇인가: 단위 테스트란

noahkim_ 2025. 1. 7. 00:14

블라디미르 코리코프 님의 "단위 테스트" 책을 정리한 포스팅입니다.


1. '단위 테스트'의 정의

  • 작은 코드 조각 단위를 독립적으로 빠르게 검증하는 테스트

 

격리성

  • 독립적으로 실행 가능한 상태
  • 순서에 의존하지 않아야 함
의존성 종류 설명 문제점 해결 방법
공유 의존성 테스트 간에 공유되는 상태나 데이터
ex) 정적 가변 필드, 싱글턴, 전역 변수
테스트 간 상호 간섭 발생 공유 제거
테스트마다 초기화
비공개 의존성 코드 흐름상 드러나지 않는 의존성
ex) JDBC 연결, 시스템 시계 접근, 숨겨진 서비스 의존성 등
숨겨진 실패 원인
(원인을 파악하기 어려움)
메모리 기반 프록시
프로세스 의존성 외부 프로세스에 의존하는 시스템 구성 요소
ex) 메시지 큐(RabbitMQ), 백그라운드 워커(Spring Batch 등)
외부 시스템이 테스트에 영향 Mock 객체 주입

 

예제) 공유 의존성

더보기
public class SharedService {
    public static int sharedCount = 0;

    public void increase() {
        sharedCount++;
    }
}
// 테스트 A
@Test
void testA() {
    SharedService service = new SharedService();
    service.increase();
    assertEquals(1, SharedService.sharedCount); // 통과
}

// 테스트 B (순서나 병렬 실행에 따라 실패 가능)
@Test
void testB() {
    assertEquals(0, SharedService.sharedCount); // 실패할 수 있음
}
  • sharedCount는 정적 변수로, 여러 테스트에서 공유됨
  • 테스트 간 간섭 발생

 

예제) 비공개 의존성

더보기
public class TimeService {
    public boolean isBusinessHour() {
        LocalTime now = LocalTime.now(); // 숨겨진 의존성
        return now.isAfter(LocalTime.of(9, 0)) && now.isBefore(LocalTime.of(18, 0));
    }
}
// 테스트
@Test
void testBusinessHour() {
    TimeService service = new TimeService();
    assertTrue(service.isBusinessHour()); // 테스트 환경의 시간에 따라 결과 달라짐
}
  • LocalTime.now()는 숨겨진 외부 의존성
  • 재현 어려움

 

예제) 프로세스 의존성

더보기
public class MessageSender {
    private final RabbitMQClient client;

    public MessageSender(RabbitMQClient client) {
        this.client = client;
    }

    public boolean send(String message) {
        return client.sendMessage(message); // 외부 시스템 의존
    }
}
// 테스트
@Test
void testSendMessage() {
    MessageSender sender = new MessageSender(new RabbitMQClient("real-host"));
    assertTrue(sender.send("hello")); // RabbitMQ 가동 여부에 따라 실패
}
  • 테스트가 실제 외부 메시지 큐 시스템에 의존

 

2. Mock

  • 테스트 대역의 한 종류
  • 테스트 중인 코드가 어떤 동작을 할 수 있도록 흉내만 내는 객체
  • 실제 상태와 관계없이 테스트가 요구하는 방식으로 요청에 응답
항목 설명
🎯 목적
의존성으로 인해 테스트가 어려운 부분을 대체
📦 동작 방식
정해진 응답을 반환하거나, 특정 메서드 호출 여부를 검증
⚙️ 상태 무관
실제 동작이나 내부 상태에 의존하지 않음
🤝 인터페이스 필요
의존성을 추상화해서 주입 가능하도록 만들기 위해 보통 인터페이스 설계가 필요

 

예제) Mock - Repository

더보기
@Test
public void testAdd() {
    // Mock 객체 생성
    ResultRepository mockRepository = Mockito.mock(ResultRepository.class);

    // 테스트 대상 클래스 생성 (Mock 주입)
    CalculatorService service = new CalculatorService(mockRepository);

    // 테스트 실행
    int result = service.add(5, 3);

    // 결과 검증
    assertEquals(8, result);

    // Mock 객체의 동작 검증
    verify(mockRepository).saveResult(8); // saveResult(8)가 호출되었는지 확인
}