일 | 월 | 화 | 수 | 목 | 금 | 토 |
---|---|---|---|---|---|---|
1 | ||||||
2 | 3 | 4 | 5 | 6 | 7 | 8 |
9 | 10 | 11 | 12 | 13 | 14 | 15 |
16 | 17 | 18 | 19 | 20 | 21 | 22 |
23 | 24 | 25 | 26 | 27 | 28 |
- 1차원 DP
- 2차원 dp
- 99클럽
- @BeforeAll
- @BeforeEach
- @Builder
- @Entity
- @GeneratedValue
- @GenericGenerator
- @NoargsConstructor
- @Query
- @Table
- @Transactional
- Actions
- Amazon EFS
- amazon fsx
- Android Studio
- ANSI SQL
- ApplicationEvent
- assertThat
- async/await
- AVG
- AWS
- Azure
- bind
- builder
- button
- c++
- c++ builder
- c03
- Today
- Total
기록
[테스트 전략] 현재시간, Private 메서드, 테스트 전용 메서드 본문
테스트 코드를 작성하다 보면 여러 가지 질문이 떠오릅니다. 그중에서도 특히 많은 고민을 불러일으키는 질문은 아래의 세 가지입니다:
- 테스트에서 외부 요소(현재 시간 등)는 어떻게 제어해야 하는가?
- Private 메서드는 테스트를 해야 하는가?
- 테스트에서만 사용하는 함수나 코드는 어떻게 처리해야 하는가?
외부 요소는 어떻게 제어해야 하는가?
외부 요소가 테스트에 미치는 영향
코드에서 외부 요소(예: 현재 시간 LocalDateTime.now, 환경 변수, 외부 API 호출 등)를 직접 호출하면, 테스트 작성이 어려워지고 유지보수에 악영향을 줄 수 있습니다. 특히 외부 요소를 직접 호출하면 다음과 같은 문제가 발생합니다:
- 테스트 결과의 불안정성 : 테스트가 실행될 때마다 현재 시간이 달라지기 때문에 결과가 일관되지 않을 수 있습니다.
- 테스트 작성과 유지보수의 어려움 : 외부 요소를 직접 사용하는 경우, 이를 Mocking하거나 대체하는 로직이 필요합니다.
한 번 LocalDateTime.now와 같은 코드를 쓰기 시작하면, 프로젝트 전반에 퍼지면서 관리가 점점 어려워집니다. 따라서 의식적으로 외부요소를 직접 포함한 로직을 지양하고, 외부에서 주입받아서 사용하도록 해야 합니다.
리팩토링: 외부 요소를 제어할 수 있도록 설계하기
잘못된 코드: 외부 요소를 직접 호출
public Order createOrder() {
LocalDateTime currentDateTime = LocalDateTime.now(); // 외부 요소 직접 호출
LocalTime currentTime = currentDateTime.toLocalTime();
if (currentTime.isBefore(SHOP_OPEN_TIME) || currentTime.isAfter(SHOP_CLOSE_TIME)) {
throw new IllegalArgumentException("주문 시간이 아닙니다. 관리자에게 문의하세요.");
}
return new Order(currentDateTime, beverages);
}
위 코드는 현재 시간에 따라 실행 결과가 달라질 수 있으며, 테스트하기도 어렵습니다.
개선된 코드: 외부 요소를 주입받아 사용
public Order createOrder(LocalDateTime currentDateTime) {
LocalTime currentTime = currentDateTime.toLocalTime();
if (currentTime.isBefore(SHOP_OPEN_TIME) || currentTime.isAfter(SHOP_CLOSE_TIME)) {
throw new IllegalArgumentException("주문 시간이 아닙니다. 관리자에게 문의하세요.");
}
return new Order(currentDateTime, beverages);
}
- LocalDateTime.now()를 호출하는 대신, 현재 시간을 매개변수로 주입받아 사용합니다.
- 이를 통해 테스트 시 고정된 값을 전달할 수 있습니다.
테스트 작성: 고정된 값 사용
개선된 테스트 코드
@DisplayName("주문 생성 시 주문 등록 시간을 기록한다.")
@Test
void registeredDateTime() {
// given
LocalDateTime fixedDateTime = LocalDateTime.of(2025, 1, 1, 10, 0); // 고정된 값 사용
List<Product> products = List.of(
createProduct("001", 1000),
createProduct("002", 2000)
);
// when
Order order = Order.create(products, fixedDateTime);
// then
assertThat(order.getRegisteredDateTime()).isEqualTo(fixedDateTime);
}
이렇게 고정된 입력값을 사용함으로써, 테스트가 언제 실행되든 동일한 결과를 얻을 수 있습니다. 또한 테스트를 읽는 사람은 고정된 입력값과 예상 결과를 보고 테스트 의도를 바로 이해할 수 있습니다.
Private 메서드는 테스트해야 하는가?
Private 메서드는 테스트하지 않는다
Private 메서드는 객체 내부의 구현 세부사항입니다. 따라서 이를 직접 테스트할 필요는 없습니다. 그 이유는 다음과 같습니다:
- Public 메서드로 간접 검증 가능 : Private 메서드는 Public 메서드를 통해 호출되므로, Public 메서드를 테스트하는 과정에서 자연스럽게 검증됩니다.
- 클라이언트 입장에서 중요하지 않음 : 클라이언트가 호출할 수 있는 것은 Public 메서드뿐입니다. Private 메서드는 클라이언트가 몰라도 되는 내부 구현입니다.
Private 메서드를 테스트하고 싶다는 생각이 들면, 설계가 복잡하다는 신호일 수 있습니다. 이 경우 Private 메서드의 책임을 작은 객체로 분리하는 것을 고려해야 합니다.
테스트에서만 사용하는 코드는 어떻게 처리할까?
Request DTO와 테스트 전용 생성자
테스트를 작성하다 보면, 테스트에서만 필요한 데이터 생성자가 필요할 때가 있습니다. 예를 들어, Request DTO를 생성하기 위해 테스트 전용 생성자를 추가할 수 있습니다. 이러한 생성자나 getter 같은 간단한 함수들은 프로덕션 코드에 추가되어도 괜찮습니다.
결론
- 외부 요소는 제어 가능하도록 설계하자 :LocalDateTime.now와 같은 외부 요소는 최상위 레벨에서 주입받아야 한다.
- Private 메서드 테스트는 설계의 신호일 수 있다 : Private 메서드를 테스트하지 말고, 작은 객체로 분리해 Public 메서드로 검증하자.
- 테스트 전용 코드는 효율적으로 관리하자 : 테스트 전용 생성자나 빌더 패턴을 활용해 테스트 데이터를 간결하게 관리하자.
'교육 > 강의' 카테고리의 다른 글
[테스트전략] 테스트 수행 시간을 줄이기 위한 환경 통합 (0) | 2025.02.11 |
---|---|
[테스트전략] Test Fixture 독립성 보장 (0) | 2025.02.10 |
[테스트전략] 각 테스트는 하나의 목적만 가진다 (0) | 2025.02.09 |
[Mock] 스프링 환경에서 외부 시스템 테스트하기 (0) | 2025.02.06 |
[JpaTest] CQRS와 서비스 레이어에서의 트랜잭션 관리 (0) | 2025.02.05 |