Junit 5 – @TestInstance
Junit5 @TestInstance Annotation 과 @TestInstance Annotation을 사용하여 테스트 인스턴스 수명 주기 동작을 변경하는 방법에 대하여 알아보도록 하겠습니다.
1. Junit 5 테스트 인스턴스 수명 주기
JUnit은 각 테스트 메서드를 실행하기 전에 각 테스트 클래스의 새 인스턴스를 만듭니다.
Junit5 테스트 인스턴스 수명 주기의 기본 동작은 '메소드별'입니다.
이것은 @TestInstance 주석을 사용하여 변경할 수 있습니다.
Junit 5는 두 가지 인스턴스 수명 주기 모드를 지원합니다.
1. Lifecycle per-method(메서드별 수명 주기)
2. Lifecycle per-class(클래스별 수명 주기)
테스트 클래스에 @TestInstance Annotation을 추가하여 테스트에 대한 인스턴스 수명 주기 모드를 설정할 수 있습니다.
2. Lifecycle per-method mode (메서드별 수명 주기 모드)
Junit의 모든 버전에 대한 기본 동작입니다. 이 모드를 사용하면 각 테스트 방법, 테스트 팩토리 방법 또는 테스트 템플릿 방법에 대해 새 테스트 인스턴스가 생성됩니다.
@TestInstance(Lifecycle.PER_METHOD) 사용예
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
|
package com.tistory.itbaewom.instance;
import static org.junit.jupiter.api.Assertions.assertEquals;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.TestInstance;
import org.junit.jupiter.api.TestInstance.Lifecycle;
import com.tistory.itbaewom.util.Calculator;
@TestInstance(Lifecycle.PER_METHOD)
public class CalculatorTestPerMethod {
private int result = 5;
@Test
void test_addition() {
result = Calculator.addition(result, 2);
System.out.println("Calculator.addition(result, 2) ===> " + result);
assertEquals(7, result);
}
@Test
void test_subtraction() {
result = Calculator.subtraction(result, 2);
System.out.println("Calculator.subtraction(result, 2) ===> " + result);
assertEquals(3, result);
}
@Test
void test_multiplication() {
result = Calculator.multiplication(result, 2);
System.out.println("Calculator.multiplication(result, 2) ===> " + result);
assertEquals(10, result);
}
@Test
void test_division() {
result = Calculator.division(result, 2);
System.out.println("Calculator.division(result, 2) ===> " + result);
assertEquals(2, result);
}
}
|
cs |
테스트 수행 결과 입니다.
메서드 별로 객체가 생성되어 항상 result의 값이 5입니다.
5 - 2 = 3
5 * 2 = 10
5 / 2 = 2
5 + 2 = 7
3. Lifecycle per-class mode(클래스별 수명 주기 모드)
이 모드를 사용하면 테스트 클래스당 한 번씩 새 테스트 인스턴스가 생성됩니다.
3-1. 이 모드에서는 주어진 테스트 클래스의 테스트 메서드와 비정적 @BeforeAll 및 @AfterAll 간의 테스트 인스턴스 상태를 공유합니다.
@TestInstance(Lifecycle.PER_CLASS) 사용 예
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
|
package com.tistory.itbaewom.instance;
import static org.junit.jupiter.api.Assertions.assertEquals;
import org.junit.jupiter.api.AfterAll;
import org.junit.jupiter.api.AfterEach;
import org.junit.jupiter.api.BeforeAll;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.TestInfo;
import org.junit.jupiter.api.TestInstance;
import org.junit.jupiter.api.TestInstance.Lifecycle;
import com.tistory.itbaewom.util.Calculator;
@TestInstance(Lifecycle.PER_CLASS)
public class CalculatorTestPerClass {
private int result = 5;
@BeforeAll // 새 테스트 인스턴스 생성시 1회
void beforeAll() {
System.out.println(">>>> @BeforeAll : 테스트를 시작합니다.\n");
}
@BeforeEach // 메서드 테스트 실행전 마다
void beforeEach(TestInfo testInfo) {
System.out.println("@BeforeEach : " + testInfo.getTestMethod().get().getName() + "메서드 테스트 수행시작!!!");
}
@Test
void test_addition() {
result = Calculator.addition(result, 2);
System.out.println("Calculator.addition(result, 2) ===> " + result);
assertEquals(5, result);
}
@Test
void test_subtraction() {
result = Calculator.subtraction(result, 2);
System.out.println("Calculator.subtraction(result, 2) ===> " + result);
assertEquals(3, result);
}
@AfterEach // 메서드 테스트 실행후 마다
void afetrEach(TestInfo testInfo) {
System.out.println("@AfterEach : " + testInfo.getTestMethod().get().getName() + "메서드 테스트 수행종료!!!\n");
}
@AfterAll // 새 테스트 인스턴스 소멸시 1회
void afetrAll() {
System.out.println(">>>> @AfterAll : 테스트를 종료합니다.");
}
}
|
cs |
테스트 수행 결과 입니다.
클래스당 한 번씩 새 테스트 인스턴스가 생성됩니다. 그래서 result 변수의 값을 모든 데스트 메서드가 공유합니다.
5 - 2 = 3
3 + 2 = 5
@BeforeAll : 객체 생성시 1회만 실행됩니다.
@AfterAll : 객체 소멸시 1회만 실행됩니다.
@BeforeEach : 테스트 메서드(@Test 가 붙은 메서드) 실행 전에 매번 실행됩니다.
@AfterEach : 테스트 메서드(@Test 가 붙은 메서드) 실행 후에 매번 실행됩니다.
3-2. 테스트 인스턴스 수명 주기의 기본 모드는 PER_METHOD입니다.
테스트 인스턴스 수명 주기 모드를 Lifecycle.PER_CLASS로 설정하지 않은 경우 @BeforeAll 및 @AfterAll 콜백 메서드는 정적(static) 메서드 이어야 합니다. 그렇지 않으면 테스트 수행시 오류가 발생합니다.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
|
package com.tistory.itbaewom.instance;
import static org.junit.jupiter.api.Assertions.assertEquals;
import org.junit.jupiter.api.AfterAll;
import org.junit.jupiter.api.BeforeAll;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.TestInstance;
import org.junit.jupiter.api.TestInstance.Lifecycle;
import com.tistory.itbaewom.util.Calculator;
@TestInstance(Lifecycle.PER_METHOD)
public class CalculatorTestPerMethod2 {
private int result = 5;
@Test
void test_addition() {
result = Calculator.addition(result, 2);
System.out.println("Calculator.addition(result, 2) ===> " + result);
assertEquals(7, result);
}
@BeforeAll // 새 테스트 인스턴스 생성시 1회
void beforeAll() {
System.out.println(">>>> @BeforeAll : 테스트를 시작합니다.\n");
}
@AfterAll // 새 테스트 인스턴스 소멸시 1회
void afetrAll() {
System.out.println(">>>> @AfterAll : 테스트를 종료합니다.");
}
}
|
cs |
테스트 수행 결과 입니다.
다음과 같은 에러가 발생합니다.
@BeforeAll method 'void com.tistory.itbaewom.instance.CalculatorTestPerMethod2.beforeAll()' must be static unless the test class is annotated with @TestInstance(Lifecycle.PER_CLASS).
위의 예제를 변경하여 @BeforeAll 과 @AfterAll 이 붙은 메서드를 static으로 변경해 보겠습니다.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
|
package com.tistory.itbaewom.instance;
import static org.junit.jupiter.api.Assertions.assertEquals;
import org.junit.jupiter.api.AfterAll;
import org.junit.jupiter.api.AfterEach;
import org.junit.jupiter.api.BeforeAll;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.TestInfo;
import org.junit.jupiter.api.TestInstance;
import org.junit.jupiter.api.TestInstance.Lifecycle;
import com.tistory.itbaewom.util.Calculator;
@TestInstance(Lifecycle.PER_METHOD)
public class CalculatorTestPerMethod3 {
private int result = 5;
@Test
void test_addition() {
result = Calculator.addition(result, 2);
System.out.println("Calculator.addition(result, 2) ===> " + result + "\n");
assertEquals(7, result);
}
@Test
void test_subtraction() {
result = Calculator.subtraction(result, 2);
System.out.println("Calculator.subtraction(result, 2) ===> " + result);
assertEquals(3, result);
}
@BeforeAll // 새 테스트 인스턴스 생성시 1회
static void beforeAll() {
System.out.println(">>>> @BeforeAll : 테스트를 시작합니다.\n");
}
@AfterAll // 새 테스트 인스턴스 소멸시 1회
static void afetrAll() {
System.out.println(">>>> @AfterAll : 테스트를 종료합니다.");
}
@BeforeEach // 메서드 테스트 실행전 마다
void beforeEach(TestInfo testInfo) {
System.out.println("@BeforeEach : " + testInfo.getTestMethod().get().getName() + "메서드 테스트 수행시작!!!");
}
@AfterEach // 메서드 테스트 실행후 마다
void afetrEach(TestInfo testInfo) {
System.out.println("@AfterEach : " + testInfo.getTestMethod().get().getName() + "메서드 테스트 수행종료!!!\n");
}
}
|
cs |
테스트 수행 결과 입니다. 에러 없이 테스트가 수행이 됩니다.
콘솔창에 나타난 결과 입니다.
3-3. 기본적으로 Junit 5 중첩 테스트 클래스는 @BeforeAll 및 @AfterAll 메서드 작성을 허용하지 않습니다. 테스트 수명 주기 모드가 Lifecycle.PER_CLASS이고 비정적이어야 하는 경우에만 중첩 클래스에 작성할 수 있습니다.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
|
package com.tistory.itbaewom.instance;
import org.junit.jupiter.api.AfterAll;
import org.junit.jupiter.api.BeforeAll;
import org.junit.jupiter.api.Nested;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.TestInstance;
import org.junit.jupiter.api.TestInstance.Lifecycle;
// 기본적으로 Junit 5 중첩 테스트 클래스는 @BeforeAll 및 @AfterAll 메서드 작성을 허용하지 않습니다.
// 테스트 수명 주기 모드가 Lifecycle.PER_CLASS이고 비정적이어야 하는 경우에만 중첩 클래스에 작성할 수 있습니다.
public class Junit5NestedTestsTest {
@Test
void test1() {
System.out.println(">>>> test1()");
}
// 다음 두줄을 지정하지 않으면 내부클래스 테스트 수행 안함
@Nested
@TestInstance(Lifecycle.PER_CLASS)
class TestA {
@BeforeAll
void testA_BeforeAll() {
System.out.println("===> testA_BeforeAll()");
}
@Test
void test1() {
System.out.println("내부 클래스의 test1() 메서드 테스트 수행");
}
@AfterAll
void testA_AfterAll() {
System.out.println("===> testA_BeforeAll()");
}
}
}
|
cs |
테스트 수행 결과 입니다. 에러 없이 테스트가 수행이 됩니다.
콘솔창에 나타난 결과 입니다.
위의 코드를 보면 Lifecycle.PER_CLASS이고 비정적메서드 이어야 하는 경우에만 중첩 클래스에 작성할 수 있음을 알 수 있습니다.
3-4. 인터페이스 메서드에서 @BeforeAll 및 @AfterAll을 선언할 수 있습니다.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
|
package com.tistory.itbaewom.instance;
import org.junit.jupiter.api.AfterAll;
import org.junit.jupiter.api.BeforeAll;
import org.junit.jupiter.api.TestInstance;
import org.junit.jupiter.api.TestInstance.Lifecycle;
// 인터페이스 메서드에서 @BeforeAll 및 @AfterAll을 선언할 수 있습니다.
@TestInstance(Lifecycle.PER_CLASS)
public interface BaseTest {
@BeforeAll
default void beforeAll() {
System.out.println(">>>> @BeforeAll : 테스트를 시작합니다.");
System.out.println("-".repeat(80));
}
@AfterAll
default void afetrAll() {
System.out.println("-".repeat(80));
System.out.println(">>>> @AfterAll : 테스트를 종료합니다.");
}
}
|
cs |
Java8에서 인터페이스에 디폴트 메소드(Default methods)라는 것이 추가되었습니다. 인터페이스는 메소드 정의만 할 수 있고 구현은 할 수 없었습니다만, Java8부터 디폴트 메소드라는 개념이 생겨 구현 내용도 인터페이스에 포함시킬 수 있었습니다. 메서드 선언시 default 예약어를 사용 합니다.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
|
package com.tistory.itbaewom.instance;
import static org.junit.jupiter.api.Assertions.assertEquals;
import org.junit.jupiter.api.Test;
import com.tistory.itbaewom.util.Calculator;
// 인터페이스 메서드에서 @BeforeAll 및 @AfterAll을 선언할 수 있습니다.
public class BaseTestImplIntsanceLifecycle implements BaseTest {
private int result = 5;
@Test
void test_addition() {
result = Calculator.addition(result, 2);
System.out.println("Calculator.addition(result, 2) ===> " + result);
assertEquals(5, result);
}
@Test
void test_subtraction() {
result = Calculator.subtraction(result, 2);
System.out.println("Calculator.subtraction(result, 2) ===> " + result);
assertEquals(3, result);
}
}
|
cs |
테스트 수행 결과 입니다. 에러 없이 테스트가 수행이 됩니다.
콘솔창에 나타난 결과 입니다. interface에 선언한 @BeforeAll 및 @AfterAll이 실행되었습니다.
4. 기본 테스트 인스턴스 수명 주기를 전체적으로 변경
기본 테스트 인스턴스 수명 주기 모드를 변경하려면 junit.jupiter.testinstance.lifecycle.default 구성 매개변수를 TestInstance.Lifecycle에 정의된 enum 상수의 이름으로 설정하고 대소문자는 무시합니다.
src/test/resources의 클래스 경로 루트에 junit-platform.properties라는 파일을 만듭니다.
테스트 인스턴스 수명 주기 모드를 per_class로 설정
-Djunit.jupiter.testinstance.lifecycle.default=per_class
테스트 인스턴스 수명 주기 모드를 per_method로 설정합니다(기본 동작이므로 실제로 설정할 필요 없음)
-Djunit.jupiter.testinstance.lifecycle.default=per_method