Fixture Monkey란?

김민창·2022년 9월 21일
2
post-thumbnail

이게 뭐에요?

  • 재사용 가능하고 복잡한 테스트 객체를 자동으로 생성해주는 자바 라이브러리

  • 테스트 수행에 필요한 픽스쳐를 간편하게 바인딩해주고 랜덤한 값을 넣어줄 수 있는 라이브러리


왜 쓰는거죠?

  1. 테스트코드의 중요성은 제쳐두고, 테스트코드를 작성하는데 소요되는 시간이 너무 많이든다

  2. 개발하다보면 편협한 시야로, 엣지케이스를 놓치는 경우가 생긴다

  3. 다양한 테스트로 커버리지를 높이기 위해 다양한 객체생성으로 코드가 길어진다

  4. 길어진 given 단계 때문에 코드의 가독성이 떨어진다



등등 다양한 이유가 있는데,  Fixture Monkey를 사용한 코드와 그렇지 않은 코드의 차이는 다음과 같다

  • Fixture Monkey 미사용
@Test
public void getEntryCreatePostDtoTest01() throws Exception 
	//given
    PostCreateDto.Survey survey =
            PostCreateDto.Survey
                    .builder()
                    .title("survey title")
                    .question(List.of("a", "b", "t"))
                    .build();

    PostCreateDto.CreatePostRequest createPostRequest =
            PostCreateDto.CreatePostRequest
                    .builder()
                    .title("post title")
                    .text("wanna text!")
                    .isNotice(false)
                    .isRecentNotice(false)
                    .tagId(1L)
                    .uuid("1234-qwer-asdf-zxcv")
                    .hasSurvey(true)
                    .survey(survey)
                    .build();

    //when
    EnterPostCreateDto.Response result = createPostPageBusiness.getEntryCreatePostDto(createPostRequest, 1L);
    //then
    assertThat(result.getIsNotice()).isEqualTo(false);
    assertThat(result.getTime()).isEqualTo(3);
    assertThat(result.getUuid()).isEqualTo("1234-qwer-asdf-zxcv");

}

  • Fixture Monkey 사용
@Test
public void getEntryCreatePostDtoTest02() throws Exception 
	//given
    PostCreateDto.Survey survey = MonkeyUtil.monkey().giveMeOne(PostCreateDto.Survey.class);
    PostCreateDto.CreatePostRequest createPostRequest = 
							MonkeyUtil.monkey().giveMeBuilder(PostCreateDto.CreatePostRequest.class).set("survey", survey).sample();

    //when
    EnterPostCreateDto.Response result = createPostPageBusiness.getEntryCreatePostDto(createPostRequest, 1L);
    //then
    assertThat(result.getIsNotice()).isEqualTo(createPostRequest.getIsNotice()); 
    assertThat(result.getTime()).isEqualTo(3);
    assertThat(result.getUuid()).isEqualTo(createPostRequest.getUuid());

}

given 파트에서 임의의 객체를 만들어서 넣어줬었던 기존방식에서 Fixture Monkey를 활용하여 코드량이 현저하게 줄어든것을 볼 수 있다

개인적으로 생각한 Fixture Monkey의 장점은 다음과 같다

  1. Bean Validation에 맞게 테스트 할때 마다 새로운 값들이 바인딩 되기 때문에 코드의 신뢰성을 높여준다.
  2. 개발하며 생각하지도 못한 엣지케이스들을 처리할 수 있다
  3. 다양한 테스트를 하기 위해 다양한 객체를 만들더라도 Fixture Monkey에서 자동으로 만들어주니 노동력이 감소한다
  4. 위 코드에서 보았듯 given 파트의 코드가 현저히 줄어들었기 때문에 가독성이 높아진다

그래서 어떻게 쓰죠?

// 임의 Integer 생성
Integer number = FixtureMonkey.create().giveMeOne(Integer.class);

// 100 이상 임의 Integer 생성
Integer number = FixtureMonkey.create().giveMeBuilder(Integer.class)
                	.set(Arbitraries.integers().greaterOrEqual(100))

// 임의 객체 생성
Sample sample = FixtureMonkey.create().giveMeOne(Sample.class);

// 조건있는 임의 객체 생성
Sample sample = FixtureMonkey.create().giveMeBuilder(Sample.class)
										.set("title", Arbitraries.strings().ofMinLength(1))
										.set("age", notNull());

객체는 Bean Validation에 정의되어있는 대로 범위 내 객체가 자동으로 생성된다



다음과 같은 Bean Validation을 설정한 Entity가 있다고 가정한다

@Data 
public class Sample {
    @NotNull
    private Long id;
 
    @NotBlank
    private String orderNo;
 
    @Size(min = 2, max = 10)
    private String productName;
 
    @Min(1)
    @Max(100)
    private int quantity;
 
    @Min(0)
    private long price;
 
    @Size(max = 3)
    private List<@NotBlank @Size(max = 10) String> items = new ArrayList<>();
 
    @PastOrPresent
    private Instant orderedAt;
}

Fixture Monkey를 활용하여 자동으로 객체를 생성한다면 제공되는 필드값은 다음의 테스트를 만족한다

@Test
@RepeatedTest(100)
void test() {
    Sample sample = FixtureMonkey.create().giveMeOne(Sample.class);

    then(sample.getId()).isNotNull(); // @NotNull
	then(sample.getOrderNo()).isNotBlank(); // @NotBlank
	then(sample.getProductName().length()).isBetween(2, 10); // @Size(min = 2, max = 10)
	then(sample.getQuantity()).isBetween(1, 100); // @Min(1) @Max(100)
	then(sample.getPrice()).isGreaterThanOrEqualTo(0); // @Min(0)
	then(sample.getItems()).hasSizeLessThan(3); // @Size(max = 3)
	then(sample.getItems()).allMatch(it -> it.length() <= 10); // @NotBlank @Size(max = 10)
    then(sample.getOrderedAt()).isBeforeOrEqualTo(Instant.now()); // @PastOrPresent
}

Fixture Monkey Github 또는 공식문서에서 훨씬 많은 정보를 얻을 수 있다
공식문서가 약간 불친절하다고 한다

profile
개발자 팡이

1개의 댓글

comment-user-thumbnail
2024년 7월 3일

좋은 내용 잘 보고 갑니다!

답글 달기