본문 바로가기
Java

[Java] JUnit 5 사용법 (5) - @EnumSource, @MethodSource, @CvsSource

by 노력남자 2022. 9. 6.
반응형

이번 포스팅에선 이전 포스팅에서 다뤘던 @ParameterizedTest를 사용할 때 부가적으로 사용해야 하는 어노테이션 9개 중 나머지 3개를 다루겠습니다.

 

@EnumSource

 

Enum에 정의된 상수들을 테스트하기 위한 어노테이션

 

파라미터명 타입 설명
value Class<? extends Enum<?>> 테스트할 Enum 클래스

기본값 : NullEnum.class
names String[] 검색 조건 (문자열, 정규식)

mode에서 사용됌
mode Mode INCLUDE : names.contains(name) << Default
EXCLUDE : !names.contains(name))
MATCH_ANY : patterns.stream().anyMatch(name::matches)
(조건에 하나라도 부합되는 상수)
MATCH_ALL : patterns.stream().allMatch(name::matches)
(조건에 모두 부합되는 상수)

name : Enum 명
names : names 필드
patterns : names에 있는 정규식

 

메소드 인자가 Enum이 아닐 경우 에러 발생

 

org.junit.platform.commons.PreconditionViolationException: First parameter must reference an Enum type (alternatively, use the annotation's 'value' attribute to specify the type explicitly): void com.effortguy.junit5.parameterizedTestAnnotation.EnumSourceAnnotation.testEnumSourceValue2(java.lang.String)

 

예시

 

value

 

value는 optional로 없을 경우 메소드 인자에 선언된 Enum 타입이 default 값으로 주입

 

package com.effortguy.junit5.parameterizedTestAnnotation;

import com.effortguy.junit5.enumsource.Day;
import com.effortguy.junit5.enumsource.Month;
import org.junit.jupiter.params.ParameterizedTest;
import org.junit.jupiter.params.provider.EnumSource;

import static org.junit.jupiter.params.provider.EnumSource.Mode.*;
import static org.junit.jupiter.params.provider.EnumSource.Mode.MATCH_ALL;

public class EnumSourceAnnotation {

    @ParameterizedTest
    @EnumSource(value = Month.class)
    void testEnumSourceValue(Month month) {
    }

    //value 생략 시 메소드 인자 값 정보를 읽어서 주입
    @ParameterizedTest
    @EnumSource
    void testEnumSourceValue2(Month month) {
    }
}

 

 

mode = Mode.INCLUDE (Default)

 

enum name 값이 May 인 것만 테스트

 

@ParameterizedTest
@EnumSource(mode = INCLUDE, names = {"May"})
    void testEnumSourceInclude(Month month) {
}

 

 

mode = Mode.EXCLUDE

 

enum name 값이 May, March 인 것 제외하고 테스트

 

@ParameterizedTest
@EnumSource(mode = EXCLUDE, names = {"May", "March"})
void testEnumSourceExclude(Month month) {
}

 

 

mode = Mode.MATCH_ANY

 

정규식 "^.*sday$" (sday로 끝나는), "^.*esday$" (esday로 끝나는) 조건 중 하나만 만족해도 테스트

 

@ParameterizedTest
@EnumSource(mode = MATCH_ANY, names = {"^.*sday$","^.*esday$"})
void testEnumSourceMatchAny(Day day) {
}

 

 

mode = Mode.MATCH_ALL

 

정규식 "^.*sday$" (sday로 끝나는), "^.*esday$" (esday로 끝나는) 조건 모두 만족할 경우 테스트

 

@ParameterizedTest
@EnumSource(mode = MATCH_ALL, names = {"^.*sday$","^.*esday$"})
void testEnumSourceMatchAll(Day day) {
}

 

 

@MethodSource

 

factory 메소드가 리턴해주는 값을 가지고 반복 테스트하는 어노테이션

 

factory 메소드 조건

 

1. 반드시 static, 테스트 클래스에 @TestInstance(Lifecycle.PER_CLASS)가 있을 경우엔 필요없음

2. 인자가 없어야함

3. Stream 타입으로 리턴해야함

 

파라미터명 타입명 설명
value String factory 메소드 명

기본값 : 테스트 메소드명이랑 동일

 

예시

 

테스트 메소드의 인자가 1개인 경우

 

package com.effortguy.junit5.parameterizedTestAnnotation;

import org.junit.jupiter.params.ParameterizedTest;
import org.junit.jupiter.params.provider.MethodSource;

import java.util.stream.Stream;

import static org.junit.jupiter.api.Assertions.assertNotNull;

public class MethodSourceAnnotation {

    // @MethodSource에 value 명시 안 하면 테스트 메소드와 동일한 것 찾음
    @ParameterizedTest
    @MethodSource
    void testWithDefaultLocalMethodSource(String argument) {
        assertNotNull(argument);
    }

    static Stream<String> testWithDefaultLocalMethodSource() {
        return Stream.of("apple", "banana");
    }
}

 

 

테스트 메소드의 인자가 2개 이상인 경우 arguments를 사용해서 리턴해줘야합니다.

 

@ParameterizedTest
@MethodSource("stringIntAndListProvider")
void testWithMultiArgMethodSource(String str, int num, List<String> list) {
	assertEquals(5, str.length());
	assertTrue(num >=1 && num <=2);
	assertEquals(2, list.size());
}

static Stream<Arguments> stringIntAndListProvider() {
	return Stream.of(
		arguments("apple", 1, Arrays.asList("a", "b")),
		arguments("lemon", 2, Arrays.asList("x", "y"))
	);
}

 

 

@CvsSource

 

CSV (Comma Seperated Value) 형식의 데이터로 반복 테스트를 합니다.

 

파라미터명 타입 설명
value String[] CVS 형식의 데이터
delimiter char delimiter를 변경 (char 형)

delimiterString 하고 같이 사용 불가
delimiterString String delimiter를 변경 (String 형)

delimiter 와 같이 사용 불가

delimiter, delimiterString 굳이 2개를 둔 이유는 char 형으로 굳이 사용해야 하는 경우가 있어서 그런 거 같습니다.
emptyValue String CVS 데이터 중 빈 값인 경우 대체되는 값
nullValues String[] CVS 데이터 중 null 값으로 대체할 값

 

문자열은 " " (double quote), 문자열 안에 항목 구분하기 위해선 ' ' (single quote) 사용

 

'' 로 빈 값을 표현하면 "" 과 동일, '' 도 사용하지 않으면 null로 처리

 

예제 리턴 값
@CsvSource({ "apple, banana" }) "apple", "banana"
@CsvSource({ "apple, 'lemon, lime'" }) "apple", "lemon, lime"
@CsvSource({ "apple, ''" }) "apple", ""
@CsvSource({ "apple, " }) "apple", null
@CsvSource(value = { "apple, banana, NIL" }, nullValues = "NIL") "apple", "banana", null

 

예시

 

value

 

package com.effortguy.junit5.parameterizedTestAnnotation;

import org.junit.jupiter.params.ParameterizedTest;
import org.junit.jupiter.params.provider.CsvSource;

import static org.junit.jupiter.api.Assertions.*;

public class CsvSourceAnnotation {

    @ParameterizedTest
    @CsvSource(value = {
        "apple, 1",
        "banana, 2",
        "'lemon, lime', 0xF1"
    })
    void testWithCsvSourceValue(String fruit, int rank) {
        assertNotNull(fruit);
        assertNotEquals(0, rank);
    }
}

 

 

delimiter, delimiterString

 

delimiter, delimiterString 결과는 동일

 

@ParameterizedTest
@CsvSource(value = {
        "apple: 1"
}, delimiter = ':')
void testWithCsvSourceDelimiter(String fruit, int rank) {
    assertNotNull(fruit);
    assertNotEquals(0, rank);
}

@ParameterizedTest
@CsvSource(value = {
        "apple: 1"
}, delimiterString = ":")
void testWithCsvSourceDelimiterString(String fruit, int rank) {
    assertNotNull(fruit);
    assertNotEquals(0, rank);
}

 

 

emptyValue

 

빈 값을 1로 바꾸는 테스트

 

@ParameterizedTest
@CsvSource(value = {
        "apple, ''"
}, emptyValue = "1")
void testWithCsvSourceEmptyValue(String fruit, int rank) {
    assertNotNull(fruit);
    assertNotEquals(0, rank);
}

 

 

nullValues

 

apple, NL 을 null 값으로 바꾸는 테스트

 

@ParameterizedTest
@CsvSource(value = {
        "apple, NL"
}, nullValues = {"NL", "apple"})
void testWithCsvSourceNullValues(String fruit, Integer rank) {
    assertNull(fruit);
    assertNull(rank);
}

 

 

이번 포스팅에서 반복테스트 정리를 끝내려고 했는데 너무 길어져서 다음 포스팅에 나머지 2개 어노테이션을 정리하겠습니다.

 

읽어주셔서 감사합니다.

 

참고

 

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

반응형

댓글