package com.sh.hexagonal.common;
import com.sh.hexagonal.application.domain.model.Account;
import com.sh.hexagonal.application.domain.model.Account.AccountId;
import com.sh.hexagonal.application.domain.model.ActivityWindow;
import com.sh.hexagonal.application.domain.model.Money;
public class AccountTestData {
public static AccountBuilder defaultAccount() {
return new AccountBuilder()
.withAccountId(new AccountId(3000L))
.withBaselineBalance(Money.ZERO)
.withActivityWindow(new ActivityWindow(
ActivityTestData
.defaultActivity()
.build()));
}
public static class AccountBuilder {
private AccountId accountId;
private Money baselineBalance;
private ActivityWindow activityWindow;
public AccountBuilder withAccountId(AccountId accountId) {
this.accountId = accountId;
return this;
}
public AccountBuilder withBaselineBalance(Money baselineBalance) {
this.baselineBalance = baselineBalance;
return this;
}
public AccountBuilder withActivityWindow(ActivityWindow activityWindow) {
this.activityWindow = activityWindow;
return this;
}
public Account build() {
return new Account(accountId, baselineBalance, activityWindow);
}
}
}
개요
헥사고날 아키텍처 예제 클론코딩 도중, 테스트 데이터 생성을 분리한 클래스를 발견했다. 이 방식을 채택하기 전에, 코드에 미칠 영향을 생각해보았다.
얻을 수 있는 것
테스트 데이터 생성 로직을 분리하므로써,
1. 객체 지향 방법론에 맞게 설계할 수 있다.
2. 반복되는 테스트 데이터 생성 로직을 분리하여 재사용할 수 있다.
3. 유틸리티 클래스와 같이 동작하며, 테스트 데이터 생성을 편리하게 한다.
4. 도메인과 다른 패턴을 적용할 수 있다.
e.g.) 도메인 영역에는 생성자 기반 패턴을, 테스트 데이터 생성에는 빌더 패턴을 사용하며 방식을 달리 할 수 있다.
5. 테스트 의도를 직관적으로 전달할 수 있다.
e.g.)
Lombok의 @Builder 애노테이션을 사용한다면, id 필드 지정을 위한 메서드 시그니처는 다음과 같을 것이다.
public AccoutBuilder accountId(AccountId accountId)
이는 단순히 Builder를 통한 객체 생성간에 accountId 필드 값을 지정하겠다는 것을 의미한다.
이를
public AccoutBuilder withAccountId(AccountId accountId)
라는 시그니처로 명명함으로써, 주요 목적인 [테스트 수행간 id를 직접 지정하며 테스트함(with)]을 명시할 수 있다.
잃을 수 있는 것
1. 테스트 데이터 생성 클래스를 거치게 되므로, 테스트의 성공이 프로덕션 도메인에 대한 성공이라고 확정하기 어렵다.
2. 추가 로직을 작성해야 한다면, 테스트에 영향을 미칠 영향에 대해 항상 고려해야 한다.
e.g.) 테스트 데이터 클래스에만 있는 메서드, 도메인과 같은 메서드지만 동작이 다른 경우, 버전업간 동기화 누락으로 인한 불일치 등
3. 유지보수 비용이 오히려 증가할 수 있다.
도메인 객체가 변경되면 테스트 데이터 생성 클래스도 반드시 수정해야 한다.
=> 결론은 쓰려면 잘 써야하며, 최악의 경우 테스트 코드 보증을 위한 테스트 코드.. 도 생길 수 있다.
지켜야 할 것
1. 테스트 데이터 생성 클래스가 테스트 주체를 직접 대변할 수 있어야 한다.
- 테스트 코드는 기본적으로 프로덕션 환경의 신뢰성을 위한 것이므로, 해당 주체(Domain, DTO 등)를 직접 가져와 사용한다.
- 인터페이스의 역할만을 수행하며, 테스트 컨디션에 영향을 미치는 추가 로직을 작성하지 않는다.
- 필요한 기능은 최대한 주체에 정의된 것을 사용한다.
2. 외부 라이브러리 의존을 최소화한다. (선택사항)
- 단일 테스트에 집중하기 위해, 외부 라이브러리 사용으로 인한 문제 가능성을 최소화 해야한다.
Lombok 정도는 오히려 구현에 편리함을 줄 수 있겠지만, 문제가 생기지 않는다고 확정할 순 없기에 테스트간 고려할 요소가 늘어남은 여전하다.
따라서, 의존성을 최소화하고 직접 작성하는 것이 장기적으로 더 나은 유지보수를 제공할 수 있다. 개발자의 판단에 따라 사용하되, 최대한 지양하는 방향을 가져가야 한다고 생각했다.
'트러블 슈팅' 카테고리의 다른 글
ParameterizedTest 적용해보기 (0) | 2024.12.16 |
---|---|
Junit5 디펜던시 추가에 4시간을 쓴 경험 (0) | 2024.12.04 |
백준 3407번(맹세) 풀이 - Java 메모리 제한이 심한 문제 (1) | 2024.10.25 |
[VM/Ubuntn] 양방향 클립보드 공유 (0) | 2024.07.15 |