Code/Test

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

noahkim_ 2025. 1. 7. 00:14

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


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

  • 작은 코드 조각을 대상으로, 독립적으로 빠르게 검증하는 테스트

 

격리성

  • 독립적으로 실행 가능한 상태
  • ✅ 실행 환경이나 다른 테스트에 영향받지 않음
  •  순서에 의존하지 않아야 함

 

2. 의존성

  • 내 코드가 직접 사용하지만, 내가 직접 구현하지 않은 것 (외부에 기대는 요소)
  • ✅ 단위 테스트의 격리성을 보장하기 위해 실제 의존성과 함께 동작하지 않음
  • ➡️ 테스트 대역으로 대체하여 핵심 로직만 집중적으로 검증
의존성 종류 설명 문제점 예시 해결 방법
공유 의존성 테스트 간에 공유되는 상태 테스트 간 간섭 발생 - 정적 가변 필드
- 싱글턴
- 전역 변수
- 공유 제거
- 테스트마다 초기화
비공개 의존성 코드에 드러나지 않는 숨은 의존성 실패 원인 파악 어려움 - JDBC
- LocalDateTime.now()
- 프록시
- 의존성 주입
프로세스 의존성 외부 프로세스에 의존하는 시스템 구성 요소 외부 시스템이 테스트에 영향 - RabbitMQ
- 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 가동 여부에 따라 실패
}
  • 테스트가 실제 외부 메시지 큐 시스템에 의존

 

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)가 호출되었는지 확인
}