본문 바로가기
Framework/✔️JUnit5

TDD를 위한 JUnit5 사용법 4 - 테스트 반복하기 (RepeatedTest, ParameterizedTest)

by 발개발자 2022. 9. 6.
반응형

테스트를 여러 반복해서 검증하고 싶다면, JUnit5에선 두개의 어노테이션을 통해 간단하게 지원해준다.

 

@RepeatedTest

@ParameterizedTest

 

먼저, RepeatedTest에 알아보자.

 

  RepeatedTest

   테스트 반복

@RepeatedTest(10)
	void repeatTest() {
		System.out.println("test");
}

 

RepeatedTest 어노테이션을 통해 손쉽게 해당 테스트코드를 10회 반복 할 수 있다.

여기서 RepeatedTest 어노테이션에서 RepetitionInfo 객체를 파라미터 값으로 넘겨 있다. 해당 객체에는 현재 테스트의 수행횟수, 반복횟수를 있다.

 

@RepeatedTest(10)
	void repeatTest(RepetitionInfo repetitionInfo ) {
		System.out.println("test" + repetitionInfo.getCurrentRepetition()
		+ "/" + repetitionInfo.getTotalRepetitions());
}

 

 

 

  테스트명 변경

그리고, 반복되는 테스트의 명칭을 바꿔주려면 어노테이션 안에 인자 값에 명시해주면 된다.

@DisplayName("반복 테스트")
	@RepeatedTest(value = 10, name= "{displayName} {currentRepetition} / {totalRepetitions}")
	void repeatTest(RepetitionInfo repetitionInfo ) {
		System.out.println("test" + repetitionInfo.getCurrentRepetition()
		+ "/" + repetitionInfo.getTotalRepetitions());
	}

 

 

이렇게 RepeatedTest 어노테이션 안에 있는 PlaceHolder 사용하게 되면 손쉽게 테스트 명을 바꿀 있다.

 

이제 반복적인 테스트를 때마다, 다른 값을 주고 싶을때 ParameterizedTest라는 어노테이션을 사용하면 된다.

 

  ParameterizedTest

@ParameterizedTest
	@ValueSource(strings = {"날씨가", "많이", "추워지고", "있네요."})
	void parameterizedTest(String message) {
		System.out.println(message);
}

위와 같이 ValueSource 사용하여 4개의 배열 값을 순차적으로 반복시키게 된다.

 

RepeatedTest 마찬가지로 테스트 이름을 변경하려면 아래와 같이 간단하게 소스를 수정하면 된다.

@DisplayName("파라미터 테스트")
@ParameterizedTest(name= "{index} {displayName}")
@ValueSource(strings = {"날씨가", "많이", "추워지고", "있네요."})
void parameterizedTest(String message) {
	System.out.println(message);
}

 

 

ParameterizedTest 아래의 어노테이션들을 통해 다양한 파라미터값을 지원해준다.

 

  • @ValueSource
  • @NullSource, @EmptySource, @NullAndEmptySource
  • @EnumSource
  • @MethodSource
  • @CvsSource
  • @CvsFileSource
  • @ArgumentSource

몇가지 핵심 어노테이션만 알아보자!

 

  @NullSource, @EmptySource, @NullAndEmptySource

 

Null값과 빈값을 파라미터로 전달한다.

@NullSource, @EmptySource 두개의 어노테이션을 @NullAndEmptySource 하나로 축약해서 사용할 있다.

 

@DisplayName("파라미터 테스트")
@ParameterizedTest(name= "{index} {displayName} message= {0}")
@ValueSource(strings = {"날씨가", "많이", "추워지고", "있네요."})
@NullSource
@EmptySource
void parameterizedTest(String message) {
	System.out.println(message);
}

 

 

  @ValueSource, @CvsSource

값을 전달해 주는 어노테이션으로 사용된다.

 

여기서 ParameterizedTest는 인자 값 타입에 대한 두가지의 형변환이 존재한다.

  • 암묵적인 타입변환
  • 명시적인 타입 변환

암묵적인 타입변환은 기본적으로 string으로 입력된 파라미터 값을 자동으로 형변환하여 반환해주는 것이다.

해당 Case에 대한 가이드는 아래 레퍼런스를 참조하면 될 것 같다.

https://junit.org/junit5/docs/current/user-guide/#writing-tests-parameterized-tests-argument-conversion

 

JUnit 5 User Guide

Although the JUnit Jupiter programming model and extension model do not support JUnit 4 features such as Rules and Runners natively, it is not expected that source code maintainers will need to update all of their existing tests, test extensions, and custo

junit.org

 

만약 개발자가 생성한 클래스와 같은 객체로 형변환을 해주고 싶을경우, SimpleArgumentConverter 상속 받은 구현체를 제공해야 한다. 이런 경우를 명시적인 타입 변환이라 한다.

 

 

그러면 예외적으로 개발자가 생성한 클래스로 형변환을 하는 구현체를 만들어보자.

@DisplayNameGeneration(DisplayNameGenerator.ReplaceUnderscores.class)
class StudyTest {

	static class SutdyConverter extends SimpleArgumentConverter{

		@Override
		protected Object convert(Object source, Class<?> targetType) throws ArgumentConversionException {
			assertEquals(Study.class, targetType, "Can only convert to study");
			return new Study(Integer.parseInt(source.toString()));
		}
		
	}
	
	@DisplayName("파라미터 테스트")
	@ParameterizedTest(name= "{index} {displayName} message= {0}")
	@ValueSource(ints = {10,20,40})
	void parameterizedTest(@ConvertWith(SutdyConverter.class) Study study) {
		System.out.println(study.getLimit());
	}
	
	
}

위와 같이 구현체를 생성하고, @ConvertWith라는 어노테이션에 구현체를 명시해주면 손쉽게 Integer타입의 Paramter값들이 Study 객체로 형변환이 일어난다.

 

SimpleArgumentConverter 하나의 파라미터 값을 전달받을 위와 같이 사용할 있고, 만약 2 이상의 파라미터를 전달 받는다면 @CsvSource 사용하면 된다.

 

 

  Study.class 수정

public class Study {

	private StudyStatus status;// = StudyStatus.DRAFT;
	
	private int limit;
	
	private String name;
	
	public Study(int limit) {
		if(limit < 0) {
			throw new IllegalArgumentException("limit은 0보다 커야합니다!");
		}
		this.limit = limit;
	}
	
	public Study(int limit, String name) {
		this.limit = limit;
		this.name = name;
		
	}
	
	public StudyStatus getStatus() {
		return this.status;
	}
	
	public int getLimit() {
		return this.limit;
	}

	public String getName() {
		return name;
	}

	@Override
	public String toString() {
		return "Study [status=" + status + ", limit=" + limit + ", name=" + name + "]";
	}
 }

 

  Tests.class

@DisplayName("파라미터 테스트")
@ParameterizedTest(name= "{index} {displayName} message= {0}")
@CsvSource({"10, '자바 스터디'", "20, 스프링"})
void parameterizedTest(Integer limit, String name) {
	Study study = new Study(limit, name);
	System.out.println(study);
}

 

위와 같이, @CsvSource를 사용하여 2개의 파라미터 값을 전달 받을 수 있다.

만약 파라미터를 하나의 객체로 전달 받고 싶다면 ArgumentsAccessor 파라미터로 전달 받으면 된다.

 

@DisplayName("파라미터 테스트")
@ParameterizedTest(name= "{index} {displayName} message= {0}")
@CsvSource({"10, '자바 스터디'", "20, 스프링"})
void parameterizedTest(ArgumentsAccessor argumentsAccessor) {
	Study study = new Study(argumentsAccessor.getInteger(0), argumentsAccessor.getString(1));
	System.out.println(study);
}

ArgumentsAccessor 객체를 파라미터로 전달 받아, 해당 객체 안에서 차례대로 인자 값을 추출할 수 있다.

 

만약, 파라미터 자체를 개발자가 생성한 클래스인 Study 전달 받고 싶다면, 커스텀한 Aggregator 구현하면된다.

	static class StudyAggregator implements ArgumentsAggregator{

		@Override
		public Object aggregateArguments(ArgumentsAccessor accessor, ParameterContext context)
				throws ArgumentsAggregationException {
			Study study = new Study(accessor.getInteger(0), accessor.getString(1));
			return study;
		}
		
	}
	
	@DisplayName("파라미터 테스트")
	@ParameterizedTest(name= "{index} {displayName} message= {0}")
	@CsvSource({"10, '자바 스터디'", "20, 스프링"})
	void parameterizedTest(@AggregateWith(StudyAggregator.class) Study study) {
		System.out.println(study);
	}

AggregateWith 구현한 Aggregator 명시하면 손쉽게 형변환이 일어난다.

 

 

 

반응형

댓글