JUnit5 #3
Junit5 User Guide를 읽어보면서 Junit5를 공부하는 게시글입니다.
#1 바로가기, #2 바로가기
이번 게시글에서는 JUnit5 User Guide의 Writing Tests 중 나머지 부분들에 대해 알아보겠습니다.
1. Dependency Injection for Constructors and Methods
- JUnit5 이전까지는 테스트 생성자와 메서드에 매개변수를 가질 수 없었습니다.
- Junit5에서는 테스트 생성자와 메서드에 매개변수를 가질 수 있고 이는 Dependency Injection을 가능하게 합니다.
- ParameterResolver는 런타임 시 매개변수를 정의하는 특징이 있습니다.
- JUnit5 Jupiter에서는 등록된 ParameterResolver에 의해 매개변수를 사용할 수 있습니다.
- Jupiter에 등록된 ParameterResolver는 총 3가지로 TestInfo, RepetitionInfo, TestReporter ParameterResolver가 존재합니다.
- 해당 ParameterResolver에 의해 TestInfo, RepetitionInfo, TestReporter를 매개변수로 가질 수 있습니다.
1) TestInfo ParameterResolver
- TestInfo에는 해당 테스트의 DisplayName, Tags, Class, Method에 대한 정보를 가지고 있습니다.
import org.junit.jupiter.api.*;
import java.util.Set;
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertTrue;
@DisplayName("테스트 인포")
public class TestInfoTest {
TestInfoTest(TestInfo testInfo){
assertEquals("테스트 인포", testInfo.getDisplayName());
}
@BeforeEach
void beforeEach(TestInfo testInfo){
String displayName = testInfo.getDisplayName();
assertTrue(displayName.equals("First Test") || displayName.equals("secondTest()"));
}
@Test
@DisplayName("First Test")
@Tag("tag-Info")
void firstTest(TestInfo testInfo){
String displayName = testInfo.getDisplayName();
Set<String> tags = testInfo.getTags();
assertTrue(displayName.equals("First Test") && tags.contains("tag-Info"));
}
@Test
void secondTest(TestInfo testInfo){
}
}
- 생성자에 TestInfo를 통해 클래스의 DisplayName 혹은 Tag들을 테스트할 수 있습니다.
- 각 테스트에서 혹은 LifeCycle메서드에서 TestInfo를 사용하여 각 테스트들의 Tag와 DisplayName을 테스트할 수 있습니다.
- 이외에도 TestInfo에는 해당 테스트의 Class, Method들의 정보를 가져올 수 있습니다.
2. TestReporter ParameterResolver
- 테스트 시 추가적인 데이터들을 entry에 추가할 수 있는 기능 같아 보입니다.
- 보고서 같은 곳에 추가할 데이터를 추가시킬 수 있는 거 같습니다.
3. RepetitionInfo ParameterResolver
- RepetitionInfo는 Repetition테스트에서 사용할 수 있는 매개변수입니다.
- 자세한 내용은 Repetition테스트 기능을 살펴볼 때 알아보도록 하겠습니다.
2. Test Interfaces and Default Methods
- Interface의 Default Method를 이용하여 Test Lifecycle Method들을 정의할 수 있습니다.
import org.junit.jupiter.api.*;
@TestInstance(TestInstance.Lifecycle.PER_CLASS)
interface InterfaceTest {
@BeforeAll
default void beforeAll(){
System.out.println("============== InterfaceTest Before All =============== ");
}
@BeforeEach
default void beforeEach(){
System.out.println("============== InterfaceTest Before Each =============== ");
}
@AfterEach
default void afterEach(){
System.out.println("============== InterfaceTest After Each =============== ");
}
@AfterAll
default void afterAll(){
System.out.println("============== InterfaceTest After All =============== ");
}
}
- Interface로 정의된 테스트는 @Test LifeCycle을 Per_Class로 설정해주어야 합니다.
- 이제 해당 테스트를 구현하는 다른 테스트는 해당 인터페이스 테스트의 라이프사이클을 물려받습니다.
import org.junit.jupiter.api.*;
class SampleTest implements InterfaceTest {
@BeforeAll
void BeforeAll() {
System.out.println("------------- SampleTest Before All -------------");
}
@BeforeEach
void sample_BeforeEach() {
System.out.println("------------- SampleTest Before Each -------------");
}
@Test
void sampleTest(){
System.out.println(">>>>>>>>>>>>>>>>>>>>>>> Sample Test <<<<<<<<<<<<<<<<<<<<<<");
}
@AfterEach
void sample_AfterEach() {
System.out.println("------------- SampleTest After Each -------------");
}
@AfterAll
void sample_AfterAll() {
System.out.println("------------- SampleTest After All -------------");
}
}
// ========== 출력 ==========
============== InterfaceTest Before All ===============
------------- SampleTest Before All -------------
============== InterfaceTest Before Each ===============
------------- SampleTest Before Each -------------
>>>>>>>>>>>>>>>>>>>>>>> Sample Test <<<<<<<<<<<<<<<<<<<<<<
------------- SampleTest After Each -------------
============== InterfaceTest After Each ===============
------------- SampleTest After All -------------
============== InterfaceTest After All ===============
- 인터페이스 테스트를 구현하고 해당 테스트들을 정의한 후 실행해보면 위와 같이 출력이 나타나는 것을 확인할 수 있습니다.
3. Repeated Tests
- JUnut5 Jupiter는 RepeatedTest Annotation을 통해 해당 테스트의 반복 횟수를 지정하여 반복되는 테스트를 정의할 수 있습니다.
- 해당 테스트들은 일반 Test Annotation과 생명주기가 동일합니다.
package repe;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.RepeatedTest;
class MyRepeatedTest {
@BeforeEach
void beforeEach(){
System.out.println("====== BeforeEach ======");
}
@RepeatedTest(3)
void sampleTest(){
System.out.println("------ SampleTest ------");
}
}
// ============ 출력 ============
====== BeforeEach ======
------ SampleTest ------
====== BeforeEach ======
------ SampleTest ------
====== BeforeEach ======
------ SampleTest ------
- RepeatedTest Annotation으로 테스트를 반복한 후 출력을 확인해보면 각 반복되는 테스트 바닥 Before Each가 적용되는 것을 통해 생명주기가 같다는 것을 확인할 수 있습니다.
RepetitionInfo 사용하기
- RepetitionInfo를 통해 RepeatedTest의 현재, 총테스트의 수를 가져올 수 있습니다.
package repe;
import org.junit.jupiter.api.*;
class MyRepeatedTest {
@BeforeEach
void beforeEach(RepetitionInfo repetitionInfo, TestInfo testInfo){
int currentRepetition = repetitionInfo.getCurrentRepetition();
int totalRepetitions = repetitionInfo.getTotalRepetitions();
String methodName = testInfo.getTestMethod().get().getName();
System.out.printf("%s 테스트 실행 %d 번째 중 %d 번째 \n", methodName, totalRepetitions, currentRepetition);
}
@RepeatedTest(3)
void sampleTest(){
System.out.println("------- 테스트 수행 중 -------\n");
}
}
// ================ 출력 =================
sampleTest 테스트 실행 3 번째 중 1 번째
------- 테스트 수행 중 -------
sampleTest 테스트 실행 3 번째 중 2 번째
------- 테스트 수행 중 -------
sampleTest 테스트 실행 3 번째 중 3 번째
------- 테스트 수행 중 -------
- BoforeEach에서 RepetitionInfo와 TestInfo를 통해 각 반복 테스트의 현재 테스트 수, 총 테스트 수, 테스트 메서드의 이름을 가져옵니다.
- 그리고 해당 테스트에 맞게 출력을 표현해주면 위와 같이 출력이 나타나는 것을 알 수 있습니다.
4. Parameterized Tests
- ParameterizedTest Annotation을 통해 파라미터가 있는 테스트를 진행할 수 있습니다.
- ValueSource를 통해 해당 파라미터의 값을 바인딩시킬 수 있습니다.
package repe;
import org.junit.jupiter.params.ParameterizedTest;
import org.junit.jupiter.params.provider.ValueSource;
class ParamTest {
@ParameterizedTest
@ValueSource(strings = {"A", "B", "C"})
void stringTest(String str){
System.out.print(str + " "); // A B C
}
@ParameterizedTest
@ValueSource(ints = {1, 2, 3})
void intsTest(int i){
System.out.print(i + " "); // 1 2 3
}
}
- 위와 같이 테스트의 파라미터에 값을 바인딩시킬 수 있습니다.
- 모든 기본 타입뿐만 아니라 String, Class계열의 데이터도 가능합니다.
5. Timeouts
- 테스트의 제한시간을 설정할 수 있습니다.
- 테스트 시간은 초뿐만 아니라 나노 시간, 마이크로 시간, 분, 시, 일 등 다양하게 설정이 가능합니다.
package repe;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.Timeout;
import java.util.concurrent.TimeUnit;
class TimeoutTest {
@Test
@Timeout(3) // 3초
void timeoutTest() throws InterruptedException {
Thread.sleep(2000);
}
@Test
@Timeout(value = 100, unit = TimeUnit.MILLISECONDS)
void sampleTest() throws InterruptedException {
Thread.sleep(99);
}
@Test
@Timeout(3) // 3초
void fail_timeoutTest() throws InterruptedException {
Thread.sleep(4000);
}
@Test
@Timeout(value = 100, unit = TimeUnit.MILLISECONDS)
void fail_sampleTest() throws InterruptedException {
Thread.sleep(101);
}
}
- 따로 unit을 설정하지 않으면 초로 계산됩니다.
- unit을 통해 다양한 시간 타입을 설정할 수 있습니다.
- 해당 테스트에서 위쪽에 있는 두 개의 테스트는 시간 내에 동작하므로 통과되지만 아래쪽은 실패하게 됩니다.
6. Parallel Execution
- JUnit5 Jupiter는 기본적으로 테스트를 단일 스레드에서 순차적으로 실행합니다.
- 필요에 따라 병렬로 실행하도록 설정할 수 있습니다.
- 해당 기능을 사용할 일이 없을 거 같아 따로 알아보지는 않고 아래와 같이 공유 데이터 사용 시 Lock을 걸 수 있다는 정도만 이해하고 넘어가겠습니다.
@Test
@ResourceLock(value = "TEMP", mode = ResourceAccessMode.READ_WRITE)
void resourceLockTest(){
}
마무리
- JUnit5 User Guide의 Writing Tests Section에 대해 알아보았습니다.
- 뒷부분의 내용들은 보통 테스트 작성 시 사용되지 않을 거라 생각이 되지만 언젠가 필요해질 때 유용하게 사용할 수 있을 거 같습니다.
- 다음 게시글에서는 AssertJ 문서를 읽어보면서 기능들에 대해 알아보겠습니다.
'JAVA' 카테고리의 다른 글
이펙티브 자바: 아이템 2. 생성자에 매개변수가 많다면 빌더를 고려하라 (0) | 2020.02.17 |
---|---|
이펙티브 자바: 아이템1. 생성자 대신 정적 팩토리 메서드를 고려하라 (0) | 2020.02.17 |
JUnit5 User Guide 공부 #2 (0) | 2020.02.16 |
JUnit5 User Guide 공부 #1 (0) | 2020.02.16 |
Java의 String과 interning, 플라이웨이트 패턴 (0) | 2020.01.20 |