테스트코드

여러 개의 파라미터를 이용한 JUnit 테스트 @ParameterizedTest, @MethodSource

donggi 2022. 8. 9. 19:03

알고리즘을 풀 때 단위 테스트를 통해 매 실행시 케이스 입력의 귀찮음을 최소화하고자 했습니다.

 

@ParameterizedTest

- @ParameterizedTest 어노테이션을 사용하여 여러 개의 파라미터를 하나의 테스트를 통해 확인할 수 있습니다

- @Test 어노테이션 대신 @ParameterizedTest를 명시해주면 됩니다

- @ParameterzedTest를 사용하기 위해서는 하나 이상의 Source 어노테이션을 붙여줘야합니다

  - Source 어노테이션에는 @ValueSource, @MethodSource, @NullSource, @EmptySource 등등이 있습니다

 

@MethodSource

저는 2차원 배열의 케이스를 입력하고자 @MethodSource 어노테이션을 사용하게 됐습니다.

- @MethodSource 어노테이션은 테스트 클래스 또는 외부 클래스(?!!!)의 팩토리 메서드를 참조할 수 있습니다.

 

팩토리 메서드 조건

- 반드시 static 으로 만들어줘야합니다

  - 단, @TestInstance(Lifecycle.PER_CLASS (테스트의 생명주기와 관련된 어노테이션)가 있을 경우엔 필요 없습니다

- 인자는 없어야합니다

- Stream 타입으로 반환해주어야합니다

 

테스트 메서드의 인자가 하나일 경우

public class ParameterizedTestEx {
    
    @ParameterizedTest
    @MethodSource("hello")
    void parameterizedTest() {
    }
    
    static Stream<String> hello() {
        return Stream.of("hi", "hello");
    }
}

- Stream의 of() 메서드를 이용해 파라미터로 사용하고 싶은 인자를 넣어줍니다

 

인자가 여러 개일 경우

public class ParameterizedTestEx {
    
    @ParameterizedTest
    @MethodSource("hello")
    void parameterizedTest() {
    }
    
    static Stream<int[][]> testWithMultiArray() {
        return Stream.of(
            new int[][]{
                    {1, 0, 1, 1, 1}, {1, 0, 1, 0, 1}, {1, 0, 1, 1, 1}, {1, 1, 1, 0, 0}, {0, 0, 0, 0, 1}}
            },
            new int[][] {
                    {1, 0, 1, 1, 1}, {1, 0, 1, 0, 1}, {1, 0, 1, 1, 1}, {1, 1, 1, 0, 1}, {0, 0, 0, 0, 1}
            }
        );
    }
}

잘못된 예를 보여주기 위한 예시입니다

- 인자가 여러 개인 것 중 제가 사용하고 싶었던 인자 값은 int[][] 였습니다. 하지만 JUnit Jupiter 5.3.1 버전부터 다차원 배열을 Stream 에 반환할 수 없게 되었습니다

public class ParameterizedTestEx {
    
    @ParameterizedTest
    @MethodSource("hello")
    void parameterizedTest() {
    }
    
    static Stream<Object[]> testWithMultidimensionArray() {
        return Stream.of(
            new Object[]{
                new int[][]{
                    {1, 0, 1, 1, 1}, {1, 0, 1, 0, 1}, {1, 0, 1, 1, 1}, {1, 1, 1, 0, 0}, {0, 0, 0, 0, 1}}
            },
            new Object[] {
                new int[][] {
                    {1, 0, 1, 1, 1}, {1, 0, 1, 0, 1}, {1, 0, 1, 1, 1}, {1, 1, 1, 0, 1}, {0, 0, 0, 0, 1}
                }
            }
        );
    }
}

- stackoverflow 를 통해 Obejct[] 에 다차원 배열을 생성하여 반환해주는 방법이 있다는 걸 알게되었습니다

 

 

이렇게 하면 테스트를 위한 인자는 다 준비했다고 생각했습니다.

하지만 @ParameterizedTest 의 인자들은 실행 순서가 보장되지 않는 것 같았습니다

- 이처럼 @ParameterizedTest 를 통해 여러 개의 인자 값을 받고 예상되는 결과 값을 테스트 해보았으나 테스트가 틀리는 문제가 발생합니다

 

해결 방법

코드스쿼드 동료 나단의 도움으로 해결할 수 있었습니다!

static Stream<Arguments> testWithMultiArray() {
    return Stream.of(
        arguments(new int[][]{{1, 0, 1, 1, 1}, {1, 0, 1, 0, 1}, {1, 0, 1, 1, 1}, {1, 1, 1, 0, 0}, {0, 0, 0, 0, 1}}, -1),
        arguments(new int[][] {{1, 0, 1, 1, 1}, {1, 0, 1, 0, 1}, {1, 0, 1, 1, 1}, {1, 1, 1, 0, 1}, {0, 0, 0, 0, 1}}, 11)
    );
}

- 반환 값으로 Stream 에 Arguments 의 팩토리 메서드인 arguments 에 테스트 할 케이스와 해당 케이스가 반환할 값을 넣어주면 되는 문제였습니다

@ParameterizedTest
@MethodSource()
void testWithMultiArray(int[][] arr, int result) {
    assertThat(s.solution(arr)).isEqualTo(result);
}

- 위처럼 테스트 메서드의 인자 값으로 arguments 에 입력했던 타입과 변수명을 각각 입력하고 테스트를 하면 의도대로 테스트가 통과하는 걸 볼 수 있었습니다

 

 

 

 

 

https://gmlwjd9405.github.io/2019/11/27/junit5-guide-parameterized-test.html

https://junit.org/junit5/docs/5.8.1/api/org.junit.jupiter.params/org/junit/jupiter/params/provider/MethodSource.html

https://stackoverflow.com/questions/53133979/can-multidimensional-arrays-be-passed-via-methodsource-junit-5-1

https://effortguy.tistory.com/117

https://junit.org/junit5/docs/current/user-guide/#writing-tests-display-name-generator

https://junit.org/junit5/docs/5.2.0/api/org/junit/jupiter/params/provider/Arguments.html