42GG 프로젝트
UpdateDto를 nullable 처리
어제에 이어서 관리자 엔티티 업데이트 기능을 구현하다가 모든 필드를 @NotNull로 처리하기로 결정해서, NULL 예외처리를 하지 않고 진행중이었다. 하지만 테스트 코드를 작성하면서 결국 UpdateDto의 필드는 Nullable하게 처리하는게 좋을 것 같다는 결론을 내렸다.
NULL 예외처리를 하지 않으면서 발생한 가장 큰 문제는 TestFixture를 구성할 때 DTO의 모든 컬럼에 대해 NULL이 아닌 실제 값을 넣어야한다는 것이다.
public void updateLocation(Location location, List<AgendaTeam> teams) {
boolean conflictAgendaLocation = teams.stream().map(AgendaTeam::getLocation)
.anyMatch(teamLocation -> !Location.isUnderLocation(location, teamLocation));
if (conflictAgendaLocation) {
throw new InvalidParameterException(AGENDA_UPDATE_LOCATION_CONFLICT);
}
this.location = location;
}
예를 들어 위와 같은 코드에서 DTO에 NULL이 허용된다면 NullPointException이 발생한다. 따라서 Mock Request를 만든다면 모든 필드에 적절한 값을 할당해줘야한다.
public static AgendaAdminUpdateReqDto createMockAgendaUpdateReqDtoWithLocation(Location location) {
return AgendaAdminUpdateReqDto.builder()
.agendaTitle("Updated title")
.agendaContents("Updated content")
.agendaPoster("Updated posterUri")
.isOfficial(false)
.isRanking(false)
.agendaStatus(AgendaStatus.CONFIRM)
.agendaDeadLine(LocalDateTime.now())
.agendaStartTime(LocalDateTime.now().plusDays(3))
.agendaEndTime(LocalDateTime.now().plusDays(5))
.agendaLocation(location)
.agendaMinTeam(2)
.agendaMaxTeam(20)
.agendaMinPeople(2)
.agendaMaxPeople(20)
.build();
}
위와 같이 각 테스트 마다 적절한 UpdateDto를 생성하면 코드 가독성이 저하되고, UpdateDto를 TestFixture로 구성하게 되면 검증해야하는 부분에 집중할 수 없게된다.
public static AgendaAdminUpdateReqDto createMockAgendaUpdateReqDtoWithLocation(Location location) {
return AgendaAdminUpdateReqDto.builder()
.agendaLocation(location)
.build();
}
만약 UpdateDto의 필드를 Nullable하게 처리한다면위와 같이 검증하는 필드에만 집중되는 코드를 작성할 수 있고, UpdateDto를 TestFixture 형태로 관리하지 않아도 되기 때문에 효율적으로 테스트 코드를 작성할 수 있다.
결론적으로 UpdateDto는 Null 예외처리를 하는 것이 혹시 모르는 예외 상황도 방지하고, 테스트 코드를 작성하는 것에도 도움이 된다는 것을 알게되었다.
TestFixture
이번에 UpdateDto에 대해 고민해면서 TestFixture에 대한 개념을 알게되었다.
테스트 코드를 작성하면서 반복적으로 테스트를 위한 MockData를 생성하게 되는데,
여기서 TestFixture란 @BeforeEach나 @BeforeAll 같은 어노테이션을 활용하여 중복을 제거하거나, MockData를 생성하는 클래스에 메서드를 분리하여 테스트 코드의 가독성을 향상시키는 방법이다.
또한 java-test-fixture 라이브러리를 사용하면 멀티 모듈 환경에서 다른 모듈의 TestFixture를 활용할 수 있기 때문에, 모듈간의 의존성을 제거하는 동시에 코드의 재사용성을 높일 수 있다.
// build.graldle in 'gg-pingpong-api'
testImplementation testFixtures(project(':gg-utils'))
42GG 프로젝트에서도 위와 같이 java-test-fixture를 활용하고 있다.
다만 한 가지 아쉬운 점은 TestDataUtils라는 오직 하나의 클래스로 TestFixture를 관리하고 있었다는 점이다.
TestDataUtils 클래스는 약 30개의 repository를 주입받고, 약 60개의 메서드를 가지고 있는 거대한 TestFixture 클래스이다.
Match, Tournament, Game, Recruit 등 여러가지 모듈에서 사용되는 모든 Fixture를 하나의 클래스에서 관리하기 때문에 많이 비효율적이라고 생각했다.
TestFixture을 사용하는 것도 좋지만, TestFixture를 어떻게 관리해야하는지 고민이 필요해 보인다. 또한 멀티 모듈 환경에서 java-test-fixture라는 외부 의존성 없이 TestFixture을 관리하는 방법은 없는지 궁금해졌다.
TestFixture 정리 북마크
Toss - 테스트 의존성 관리로 높은 품질의 테스트 코드 유지하기
KakaoPay Tech - 실무에서 적용하는 테스트 코드 작성 방법과 노하우 Part 1: 효율적인 Mock Test
'회고록 > Project' 카테고리의 다른 글
[42GG] 데이터 플로우 다이어그램 작성 (0) | 2024.07.26 |
---|---|
[42GG] 효율적인 TestFixture 관리 (0) | 2024.07.22 |
[42GG] JPA UPDATE 로직 (영속성 컨텍스트, @DynamicUpdate, 벌크 연산) (0) | 2024.07.17 |
[42Blind] Service 계층 통합 테스트 (0) | 2024.02.25 |
[42Blind] TDD와 Github action 도입 (0) | 2024.02.05 |