테스트 코드에 대해서 배우고 많이 테스트를 작성하다보면 드는 생각이 있다.
"아니 테스트를 하기도 전에 지치겠네" 라는 생각이다.
원하는 행동을 테스트를 하려면, 테스트 하기 위한 객체들을 생성해야 한다.
예를 들면
게시글에 댓글을 작성하려면 '회원', '게시글' 이라는 두가지의 대상이 준비되어 있어야 한다.
위와 같이 두가지 객체만 생성해야 한다면 별로 어렵지 않다.
하지만 복잡한 경우 테스트 대상을 위해, 많은 객체들을 생성하게 된다.
객체를 좀 더 쉽게 만들어 보자.
테스트 픽스쳐(Test Fixture)
테스트 픽스쳐라는 단어는 다음과 같이 두 가지 공통된 의미가 있다.
1. 테스트 픽스쳐는 테스트 실행 대상 객체다. 이 객체는 정규 의존성, 즉 SUT로 전달되는 인수다. 데이터베이스에 있는 데이터나 하드 디스크의 파일일 수도 있다. 이러한 객체는 각 테스트 실행 전에 알려진 고정 상태로 유지하기 때문에 동일한 결과를 생성한다. 따라서 픽스처라는 단어가 나왔다.
2. 다른 정의는 Nuit 테스트 프레임워크에서 비롯된다. NUnit에서 Test Fixture 는 테스트가 포함된 클래스를 표시하는 특성이다.
이 글에서는 첫 번째 정의를 사용하겠다.
정의는 위와 같은데 좀 더 쉽게 풀어쓰자면, 테스트를 실행하기 위해서 해야하는 모든 것들을 테스트 픽스쳐라고 부른다.
테스트 대상의 행위를 검증하기 위해서 테스트 대상이 필요하고 테스트 대상이 의존하는 객체들이 필요하다.
이러한 객체들을 테스트 픽스쳐라고 부른다.
우선 아래 코드를 보자
@Test
@DisplayName("리뷰를 저장한다")
void saveReview() {
//given
ThumbnailImage thumb = ThumbnailImage.builder()
.thumbnailImage("이미지링크")
.build();
Member member = Member.builder()
.name("홍길동")
.email("abcd@gmail.com")
.thumbnailImage(thumb)
.build();
Cafe cafe = Cafe.builder()
.name("카페이름")
.address("주소")
.phone(01012345678)
...
.builder();
//when
reviewService.saveReview(member.getId(), cafe.getId(), new ReviewSaveRequest("커피가 맛있어요", 4.5));
List<Review> findReviews = reviewRepository.findAllByCafeId(cafe.getId());
//then
assertThat(findReviews.size()).isEqualTo(1);
}
위 코드는 카페에 리뷰가 작성이 되는지 테스트하는 코드이다.
리뷰를 저장하고 Repository에 리뷰가 1개 저장되어있는지 검증한다.
리뷰의 저장을 테스트 하기 위해서는 썸네일, 회원, 카페의 객체가 필요하다.
지금은 간단한 테스트이지만, 테스트 대상의 행위를 검증하기 위해서 준비해야하는 테스트 픽스쳐가 많다면 테스트 코드는 길어지고 가독성이 떨어지고 테스트 케이스 하나를 작성하는데 진이 다 빠져버릴 것이다.
그럼 어떻게 해야할까?
우리는 개발자다. 반복적인 과정이 싫다.
반복적인 행위를 따로 분리하자.
여기서 두가지 방법이 있다.
1. 클래스 내부에 private 팩토리 메서드를 만든다.
2. 클래스를 따로 분리하여 static 팩토리 메서드를 만든다.
private 팩토리 메서드
@Test
@DisplayName("리뷰를 저장한다")
void saveReview() {
//given
ThumbnailImage thumb = createThumbnailImage();
Member member = createMember(thumb);
Cafe cafe = createCafe();
//when
reviewService.saveReview(member.getId(), cafe.getId(), new ReviewSaveRequest("커피가 맛있어요", 4.5));
List<Review> findReviews = reviewRepository.findAllByCafeId(cafe.getId());
//then
assertThat(findReviews.size()).isEqualTo(1);
}
private ThumbnailImage createThumbnailImage() {
ThumbnailImage thumb = ThumbnailImage.builder().thumbnailImage("이미지링크").build();
thumbnailImageRepository.save(thumb);
return thumb;
}
private Member createMember(ThumbnailImage thumbnailImage) {
Member member = Member.builder()
.name("홍길동")
.email("abcd@gmail.com")
.thumbnailImage(thumbnailImage)
.build();
memberRepository.save(member);
return member;
}
private Cafe createCafe() {
Cafe cafe = Cafe.builder()
.name("카페이름")
.address("주소")
.phone(01012345678)
...
.builder();
cafeRepository.save(cafe);
return cafe;
}
위 코드와 같이 private 팩토리 메서드를 통해서 테스트 코드의 가독성이 좋아진 것을 볼 수 있다.
하지만 private 팩토리 메서드는 하나의 클래스에서만 사용이 가능하다.
다른 테스트 클래스에서도 공유하고 싶다면?
클래스를 분리 후 static 팩토리 메서드를 만들자.
클래스 분리 후 static 팩토리 메서드
public class CafeHelper {
private final CafeRepository cafeRepository = new CafeRepository();
public static Cafe persistDefaultCafe() {
Cafe cafe = Cafe.builder()
.name("카페이름")
.address("주소")
.phone(01012345678)
...
.builder();
cafeRepository.save(cafe);
return cafe;
}
public static Cafe persistCafeWithBusinessHour(List<BusinessHour> businessHours) {
Cafe cafe = Cafe.builder()
.businessHours(businessHours)
.build();
cafeRepository.save(cafe);
return cafe;
}
public static Cafe persistCafeWith24For7() {
List<BusinessHour> businessHours = makeBusinessHourWith24For7();
Cafe cafe = Cafe.builder()
.businessHours(businessHours)
.build();
return cafeRepository.save(cafe);
}
private static List<BusinessHour> makeBusinessHourWith24For7() {
List<String> daysOfWeek = List.of("MONDAY", "TUESDAY", "WEDNESDAY", "THURSDAY", "FRIDAY", "SATURDAY", "SUNDAY");
List<BusinessHour> businessHours = new ArrayList<>();
for (String day : daysOfWeek) {
businessHours.add(
BusinessHour.builder()
.day(day)
.build()
);
}
return businessHours;
}
}
위와 같이 클래스를 분리하여 static 메서드를 만들었다.
클래스를 분리해서 static 팩토리 메서드를 만들면 상황에 따른 메서드를 만들어 쉽게 테스트 코드를 작성할 수 있다.
참고
단위테스트 (블라디미르 코리코프 지음)
테스트 주도 개발 시작하기(최범균 지음)
https://velog.io/@langoustine/Test-Fixture
https://jojoldu.tistory.com/611
'테스트' 카테고리의 다른 글
| 좋지 않은 테스트 판별하기 (0) | 2024.06.10 |
|---|---|
| 테스트 코드 리팩토링 2탄 (0) | 2024.06.05 |
| [단위 테스트] 통합 테스트 (narrow integeration, wide integeration) (0) | 2024.05.17 |
| [단위 테스트] 좋은 테스트와 좋지 않은 테스트를 가르는 요인 (0) | 2024.05.13 |
| [단위 테스트] 단위 테스트의 목표 (0) | 2024.05.13 |