Spring/Test-Driven Develop

[Junit] @ParameterizedTest 객체에 값을 자동으로 넣어주는 Auto Param - @autosource

민돌v 2022. 12. 27. 20:42

 

지인이 테스트 코드를 짤 떄, 파라미터의 값을 자동으로 세팅해주는 요상한게 생겼다고 해서 사용을 해보았습니다.

강남언니 CTO 이규원님이 만든 오픈소스라고 하더군요

AutoParams는 AutoFixture에서 영감을 받은 Java의 매개변수화된 테스트를 위한 임의 테스트 데이터 생성기입니다.

 

 

Auto Param 이 제공하는 기능은, 

  • 매개변수에 값을 자동으로 주입해줍니다.
  • 리터럴 변수, Enum, Object, Generic, Collection, Primitive Type Stream 까지 지원을 해줍니다.. (굉장해)

 

@AutoSource 사용해보기 

@ParameterizedTest 와 @AutoSource 어노테이션을 사용이용해서 간편하게 사용할 수 있습니다.
Auto Param을 사용하기 위해서는 자바 8버전 이상을 사용하셔야합니다.

 

1) Install

Maven

<dependency>
  <groupId>io.github.autoparams</groupId>
  <artifactId>autoparams</artifactId>
  <version>1.1.1</version>
</dependency>

Gradle

testImplementation 'io.github.autoparams:autoparams:1.1.1'

2) 사용방법

  • 사용방법이 진짜 별거 없습니다. 아래는 @AutoSource 를 사용하지 않을 때 List 안에 일일이 객체값을 넣어주고 있습니다.
  • Test 코드임에도 불구하고, 이런게 쌓이다 보면 굉장히 귀찮고, 코드가 보기 힘들어질 때 가 있습니다.
  • @MethodSource 나 @ValueSource 를 사용하더라도 데이터를 세팅해주어야하는 것은 마찬가지입니다.

 

기존 Test 코드

  • 일일히 데이터를 세팅해주어야 함
@Test
@DisplayName("auto param test - 미사용")
void dtoByScoreSorted() {
    List<Dto> dtoList = List.of(
        new Dto(1, 1,1,1,1,1.0),
        new Dto(1, 1,1,1,1,2.0),
        new Dto(1, 1,1,1,1,5.0),
        new Dto(1, 1,1,1,1,19.0),
        new Dto(1, 1,1,1,1,13.0),
        new Dto(1, 1,1,1,1,22.0),
        new Dto(1, 1,1,1,1,4.0),
        new Dto(1, 1,1,1,1,51.0),
        new Dto(1, 1,1,1,1,3.0),
        new Dto(1, 1,1,1,1,191.0)
    );

    DtoGroup group = new DtoGroup(Dto);

    Assertions.assertThat(group.getSorted())
        .isSortedAccordingTo((o1, o2) -> o1.getScore() < o2.getScore() ? 1 : 0);
}

 

Auto Param Test 코드

  • @AutoSource 를 사용하여 데이터 세팅하는 코드가 사라지니, 조금 더 명확한 코드가 된 것만 같은 기분입니다(?)
  • Collection Type (List)를 매개변수로 주니, List 요소의 복합 Object 안에 값이 mocking 된게 아니라, 실제 값을 가지고 있었습니다.
@ParameterizedTest
@AutoSource
@Repeat(10)
@DisplayName("auto param test - 사용")
void rankInfoGroupByScoreSorted(List<Dto> dtoList) {

    DtoGroup group = new DtoGroup(dtoList);

    assertThat(group.getDtoList())
        .isSortedAccordingTo((o1, o2) -> o1.getScore() < o2.getScore() ? 1 : 0);
}

 

auto param - @autosource 예시

(아 @AutoSource 를 사용했을 떄, List 의 사이즈는 3으로 고정되던데,, 이거 사이즈를 더 늘리는 방법 아시는 분..? 흠..)

 

아무튼 굉장히 편리합니다!!


 

3) Auto Param 데이터 주입 원리

저는 복합 객체를 가지는 List 에 데이터를 주입 받아서 사용했는데, 이 복합객체에 대한 데이터 주입은 "생성자"를 사용하여 데이터를 주입한다고 합니다.

 

AutoParam 의 복합 유형 객체 생성 규칙

  1. @ConstructorProperties주석으로 장식된 생성자 가 우선적으로 선택됩니다.
  2. AutoParams는 매개변수가 가장 적은 생성자를 선택합니다.

 

✨ 운영코드에 @ConsturctorProerties 어노테이션을 삽입할 수 없으니 Test Fixture 와 함께 사용한다면, 굉장히 강력하게 사용할 수 있을거 같다는 생각이 들었습니다..!

 


 

4) 다양한 @AutoSource 사용 예시

사실 github 의 readme 내용을 거의 뱉긴거지만, 나중에 제가 볼 떄 귀찮을 거 같으니까 정리해두겠습니다.

 

타입별 AutoSource예시

1. Primitive Type - 여러 파라미터 사용하기 

@ParameterizedTest
@AutoSource
void testMethod(boolean x1, int x2, long x3, float x4, double x5, char x6) {
}

 

2. Enum Types - Enum 값 중 랜덤으로 들어감

public enum Day {
    SUNDAY, MONDAY, TUESDAY, WEDNESDAY,
    THURSDAY, FRIDAY, SATURDAY
}

@ParameterizedTest
@AutoSource
void testMethod(Day day) {
}

 

3. Generic Type, Collections, Stream 까지 지원하는 모든 타입은 매개변수로 선언하면 자동으로 값이 주입됨

@ParameterizedTest
@AutoSource
void testMethod(Map<String, ComplexObject> map, HashMap<UUID, ComplexObject> hashMap) {
}

@ParameterizedTest
@AutoSource
void testMethod(Stream<ComplexObject> stream) {
}

@ParameterizedTest
@AutoSource
void testMethod(IntStream intStream, LongStream longStream, DoubleStream doubleStream) {
}

 

 

AutoSource가 제공하는 다양한 기능 예시

1. @Fix 

  • 해당 어노테이션을 타입에 적용하면, 이후 같은 타입인 변수에는, 고정된 값이 들어감
class ValueContainer {

    private final String value;

    public ValueContainer(String value) {
        this.value = value;
    }

    public String getValue() {
        return value;
    }

}

@ParameterizedTest
@AutoSource
void testMethod(@Fix String arg1, String arg2, ValueContainer arg3) {
    assertEquals(arg1, arg2);
    assertEquals(arg1, arg3.getValue());
}

 

@AutoSrouce @Fix

 

2. @ValueAutoSource

  •  @ValueSource + @AutoSource 의 결합
  • 첫 번째 매개변수에는, @ValueSource 에서 설정한 값이 들어가고, 나머지는 @AutoSource 가 주입한다.
@ParameterizedTest
@ValueAutoSource(strings = {"foo"})
void testMethod(String arg1, String arg2) {
    assertEquals("foo", arg1);
    assertNotEquals(arg1, arg2);
}

@Fix 도 같이 사용할 수 있다고 합니다.

class ValueContainer {

    private final String value;

    public ValueContainer(String value) {
        this.value = value;
    }

    public String getValue() {
        return value;
    }

}

@ParameterizedTest
@ValueAutoSource(strings = {"foo"})
void testMethod(@Fix String arg1, String arg2, ValueContainer arg3) {
    assertEquals("foo", arg2);
    assertEquals("foo", arg3.getValue());
}

 

3. @CsvAutoSource, @MethodAutoSource 모두 동일한 특징을 가집니다.

  • @CsvAutoSource는 앞의 2개의 매개변수에 값을 주입하고
  • @MethodAutoSource 도 마찬가지로 순서대로 매개변수에 값이 지정되고, 나머지 매개변수는 값이 주입됩니다.
// @CsvAutoSource
@ParameterizedTest
@CsvAutoSource({"16, foo"})
void testMethod(int arg1, String arg2, String arg3) {
    assertEquals(16, arg1);
    assertEquals("foo", arg2);
    assertNotEquals(arg2, arg3);
}

// @MethodAutoSource
@ParameterizedTest
@MethodAutoSource("factoryMethod")
void testMethod(int arg1, String arg2, String arg3) {
    assertEquals(16, arg1);
    assertEquals("foo", arg2);
    assertNotEquals(arg2, arg3);
}

static Stream<Arguments> factoryMethod() {
    return Stream.of(
        Arguments.arguments(16, "foo")
    );
}

// @Fix + @MethodAutoSource
class ValueContainer {

    private final String value;

    public ValueContainer(String value) {
        this.value = value;
    }

    public String getValue() {
        return value;
    }

}

@ParameterizedTest
@MethodAutoSource("factoryMethod")
void testMethod(int arg1, @Fix String arg2, ValueContainer arg3) {
    assertEquals("foo", arg3.getValue());
}

static Stream<Arguments> factoryMethod() {
    return Stream.of(
        Arguments.arguments(16, "foo")
    );
}

 

4. @Min, @Max - AutoSource 의 주입되는 값 범위 지정

  • 아직 정수형 타입 (int, Integer) 만 지원되는 것 같습니다. (2022.12. 27)
@ParameterizedTest
@AutoSource
void testMethod(@Min(1) @Max(10) int value) {
    assertTrue(value >= 1);
    assertTrue(value <= 10);
}

 

5. 🔥 @Customization

  • AutoSource로 주입되는 데이터의 규칙을 정할 수 있는 거 같군요!!!! (이거 굉장하다)
  • Customizer 인터페이스를 구현하여 사용하면 되는 것 같습니다!
  • 이걸 이용하면 List의 size 도 늘릴 수 있겠네요 (하지만 귀찮으니까 패ㅆ..)

예시 : https://github.com/AutoParams/AutoParams#customization-annotation

 


4) autoparams-mockito

  • 만약 데이터 값을 세팅해줘야하는 객체가, 인터페이스를 타입으로 가지고 있는 추상클래스라면 이것또한 Mocking 할 수 있다고 합니다.
  • 정말 꼼꼼하게 만드셨네;;

예시 : https://github.com/AutoParams/AutoParams#autoparams-mockito

 

 

 

 

정말 너무 유용한 라이브러리라고 생각합니다

값을 주입해준다는게 조금 무섭긴한데, 일단은 Test 코드를 작성하는 시간이 많이 단축될거 같아 유용하게 사용하게 될거 같습니다!

이런거 만들어준 CTO 님 진짜 대단하다;; 👍

 

 

끁!


*참고

https://github.com/AutoParams/AutoParams

 

GitHub - AutoParams/AutoParams: Enjoy your TDD! AutoParams is an arbitrary test data generator for parameterized tests in Java i

Enjoy your TDD! AutoParams is an arbitrary test data generator for parameterized tests in Java inspired by AutoFixture. - GitHub - AutoParams/AutoParams: Enjoy your TDD! AutoParams is an arbitrary ...

github.com