Junit 5 시간 초과 – 테스트가 시간 내에 실행되지 않으면 실패
이번글 에서는 Junit 5 타임아웃이 무엇인지, 테스트 케이스를 작성할 때 @Timeout을 사용하는 방법, 글로벌 타임아웃을 사용하는 방법 및 여러 예제를 통해 전역적으로 타임아웃을 비활성화하는 방법을 살펴보도록 하겠습니다.
Junit 5 시간 제한을 사용하면 실행 시간이 지정된 기간을 초과하고 결과로 java.util.concurrent.TimeoutException이 발생하는 경우 테스트, 테스트 팩토리, 테스트 템플릿 또는 수명 주기 메서드가 실패하도록 선언할 수 있습니다. 시간 단위는 기본적으로 초이지만 변경할 수 있습니다.
다음은 Junit 5가 제한 시간을 지원하는 몇 가지 방법입니다.
1. @Timeout Annotation 사용.
2. assertTimeout() 또는 assertTimeoutPreemptively() 사용.
3. 구성 매개변수를 사용하여 전역 제한 시간 지정
1.1. @Timeout Annotation 사용
다음 예는 @Timeout이 테스트 메서드와 테스트 클래스 및 테스트 수명 주기에 어떻게 적용되는지 보여줍니다.
@Timeout이 클래스 수준에서 선언된 경우 테스트 메서드, 중첩 테스트 및 테스트 팩토리에 동일한 시간 제한이 적용되지만 @BeforeEach, @BeforeAll, @AfterEach, @AfterAll 수명 주기 콜백 메서드에는 시간 제한이 적용되지 않습니다.
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.timeouts;
import static org.junit.jupiter.api.Assertions.assertEquals;
import java.util.concurrent.TimeUnit;
import org.junit.jupiter.api.BeforeAll;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.Timeout;
import com.tistory.itbaewom.util.Calculator;
// 실행 시간이 3초를 초과하면 각 테스트가 실패합니다.
@Timeout(3)
public class Junit5TimeoutAnnotationClassLevelTest {
@BeforeEach
void setUp() throws Exception {
TimeUnit.SECONDS.sleep(2); // 2초대기
System.out.println("@BeforeEach");
System.out.println("-".repeat(40));
}
@BeforeAll
static void setUpBeforeAllTests() throws Exception {
TimeUnit.SECONDS.sleep(2); // 2초대기
System.out.println("@BeforeAll");
}
@Test
void test_Add() throws Exception {
TimeUnit.SECONDS.sleep(2); // 2초대기
assertEquals(5, Calculator.addition(3, 2));
System.out.println("test_addition()");
}
@Test
void test_Multiply() throws Exception {
TimeUnit.SECONDS.sleep(2); // 2초대기
System.out.println("test_Multiply()");
}
}
|
cs |
테스트 수행 결과 입니다.
콘솔창에 나타난 결과 입니다. 정상적으로 실행 되었습니다.
위의 코드에서 test_Multiply() 메서드의 코드 중에서 "TimeUnit.SECONDS.sleep(4)" 시간을 4초로 변경항 후 실행하면 테스트에 실패함을 알 수 있습니다.
다음 예는 @BeforeAll에서 @Timeout을 선언하고 시간 제한을 초과하면 실행이 중단되고 나머지 테스트 사례는 건너뛰는 예를 보여 줍니다.
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
|
package com.tistory.itbaewom.timeouts;
import static org.junit.jupiter.api.Assertions.assertEquals;
import java.util.concurrent.TimeUnit;
import org.junit.jupiter.api.BeforeAll;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.Timeout;
import com.tistory.itbaewom.util.Calculator;
public class Junit5TimeoutAnnotationTest {
@BeforeAll
// 이 메서드 실행 시간이 4초를 초과하면 모든 테스트 건너뛰기 @Timeout(4)
static void setUpBeforeAllTests() throws Exception {
TimeUnit.SECONDS.sleep(3);
System.out.println("@BeforeAll");
}
@BeforeEach
// 이 메서드 실행 시간이 4초를 초과하면 모든 테스트 실패 @Timeout(4)
void setUp() throws Exception {
TimeUnit.SECONDS.sleep(3);
System.out.println("@BeforeEach");
}
@Test
// 실행 시간이 3초를 초과하면 이 테스트는 실패합니다. @Timeout(3)
void test_Add() throws Exception {
TimeUnit.SECONDS.sleep(2);
System.out.println("test_Add()");
}
@Test
// 실행 시간이 3초를 초과하면 이 테스트는 실패합니다.
@Timeout(3) void test_Multiply() throws Exception {
TimeUnit.SECONDS.sleep(2);
System.out.println("test_Multiply()");
}
@Test
// 실행 시간이 900밀리초를 초과하면 실패 @Timeout(value = 900, unit = TimeUnit.MILLISECONDS) void test_isPrime() throws Exception {
TimeUnit.MILLISECONDS.sleep(1000);
assertEquals(10, Calculator.multiplication(5, 2));
System.out.println("test_isPrime()");
}
}
|
cs |
테스트 수행 결과 입니다.
콘솔창에 나타난 결과 입니다.
1.2. assertTimeout() 또는 assertTimeoutPreemptively()
1.2.1. assertTimeout() – 테스트 메서드는 시간 내에 실행되지 않으면 실패하지만 assertTimeout()은 실행 프로세스를 완료합니다.
1.2.2. assertTimeoutPreemptively() – 시간 내에 실행되지 않으면 테스트 메서드가 실패하고 assertTimeoutPreemptively()는 실행 프로세스가 중단됩니다.
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
|
package com.tistory.itbaewom.timeouts;
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertTimeout;
import static org.junit.jupiter.api.Assertions.assertTimeoutPreemptively;
import java.time.Duration;
import java.util.concurrent.TimeUnit;
import org.junit.jupiter.api.Test;
import com.tistory.itbaewom.util.Calculator;
public class Junit5TimeoutAnnotationTest2 {
@Test
void test_Add() throws Exception {
assertTimeout(Duration.ofSeconds(5), () -> {
TimeUnit.SECONDS.sleep(4);
assertEquals(5, Calculator.addition(3, 2));
System.out.println("test_Add()");
});
}
@Test
void test_Multiply() throws Exception {
// 실패하지만 프로세스를 완료합니다. 출력 및 실행 기간을 참조
assertTimeout(Duration.ofSeconds(5), () -> {
TimeUnit.SECONDS.sleep(6);
assertEquals(5, Calculator.addition(3, 2));
System.out.println("test_Multiply()");
});
}
@Test
void test_assertTimeoutPreemptively() throws Exception {
// 실패하지만 프로세스 중단, 출력 및 실행 기간 참조
assertTimeoutPreemptively(Duration.ofSeconds(5), () -> {
TimeUnit.SECONDS.sleep(6);
assertEquals(5, Calculator.addition(3, 2));
System.out.println("test_assertTimeoutPreemptively()");
});
}
}
|
cs |
테스트 수행 결과 입니다.
Junit5TimeoutAnnotationTest2
test_Add() – Passed – 4.012 s
test_Multiply() – Failed – 6.013 s
test_assertTimeoutPreemptively() – Failed – 5.010 s
콘솔창에 나타난 결과 입니다.
1.2.3. @TestFactory 메서드에 @Timeout을 선언하면 팩토리 메서드가 지정된 기간 내에 반환되는지 확인하지만 팩토리에서 생성된 각 개별 DynamicTest의 실행 시간은 확인하지 않습니다.
이를 위해 assertTimeout() 또는 assertTimeoutPreemptively()를 사용할 수 있습니다.
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
|
package com.tistory.itbaewom.timeouts;
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertTimeout;
import static org.junit.jupiter.api.Assertions.assertTimeoutPreemptively;
import static org.junit.jupiter.api.DynamicTest.dynamicTest;
import java.time.Duration;
import java.util.Arrays;
import java.util.Collection;
import java.util.concurrent.TimeUnit;
import org.junit.jupiter.api.DynamicTest;
import org.junit.jupiter.api.TestFactory;
import org.junit.jupiter.api.Timeout;
import com.tistory.itbaewom.util.Calculator;
public class Junit5TimeoutAnnotationTest3 {
@TestFactory
@Timeout(5)
Collection<DynamicTest> test_dynamicTestsTimeouts() {
return Arrays.asList(
dynamicTest("1st dynamic test", () -> {
TimeUnit.SECONDS.sleep(3);
assertEquals(5, Calculator.addition(3, 2));
System.out.println("Dynamic Test 1");
}),
dynamicTest("2nd dynamic test", () -> {
TimeUnit.SECONDS.sleep(2);
assertEquals(6, Calculator.multiplication(3, 2));
System.out.println("Dynamic Test 2");
}));
}
@TestFactory
Collection<DynamicTest> test_dynamicTests_AssertTimeouts() {
return Arrays.asList(
dynamicTest("3rd dynamic test", () -> {
assertTimeout(Duration.ofSeconds(5), () -> {
TimeUnit.SECONDS.sleep(10);
assertEquals(5, Calculator.addition(3, 2));
System.out.println("Dynamic Test 3");
});
}),
dynamicTest("4th dynamic test", () -> {
assertTimeoutPreemptively(Duration.ofSeconds(5), () -> {
TimeUnit.SECONDS.sleep(10);
assertEquals(5, Calculator.addition(3, 2));
System.out.println("Dynamic Test 4");
});
}));
}
}
|
cs |
테스트 수행 결과 입니다.
- 1st dynamic test – Passed – 3.006 s
- 2nd dynamic test – Passed – 2.004 s
- 3rd dynamic test – Failed – 10.004 s
- 4th dynamic test – Failed – 5.003 s
콘솔창에 나타난 결과 입니다.
1.3. Junit 5 global timeouts
구성 매개변수를 사용하여 글로벌 타임아웃을 지정할 수 있습니다.
예를 들어 전체 테스트 애플리케이션의 모든 @BeforeEach 메서드에 대한 시간 제한을 지정하려면 구성 속성 junit.jupiter.execution.timeout.beforeeach.method.default의 값을 설정합니다.
다음은 전역 제한 시간을 지정하는 데 사용할 수 있습니다.
값은 test/resources 폴더 아래의 junit-platform.properties 파일에서 설정 할 수 있습니다.
1. junit.jupiter.execution.timeout.default – 모든 테스트 가능 및 수명 주기 메서드에 대한 기본 제한 시간입니다.
2. junit.jupiter.execution.timeout.testable.method.default – 모든 테스트 가능한 메서드에 대한 기본 제한 시간입니다.
3. junit.jupiter.execution.timeout.test.method.default – @Test 메서드의 기본 시간 제한입니다.
4. junit.jupiter.execution.timeout.testtemplate.method.default – @TestTemplate 메서드에 대한 기본 제한 시간입니다.
5. junit.jupiter.execution.timeout.testfactory.method.default – @TestFactory 메서드에 대한 기본 제한 시간입니다.
6. junit.jupiter.execution.timeout.lifecycle.method.default – 모든 수명 주기 메서드에 대한 기본 제한 시간입니다.
7. junit.jupiter.execution.timeout.beforeall.method.default – @BeforeAll 메서드의 기본 시간 제한입니다.
8. junit.jupiter.execution.timeout.beforeeach.method.default – @BeforeEach 메서드의 기본 시간 제한입니다.
9. junit.jupiter.execution.timeout.afterall.method.default – @AfterAll 메서드의 기본 시간 제한입니다.
10. junit.jupiter.execution.timeout.aftereach.method.default – @AfterEach 메서드의 기본 시간 제한입니다.
Example junit-platform.properties :
junit.jupiter.execution.timeout.beforeeach.method.default=200 ms junit.jupiter.execution.timeout.test.method.default=10 s |
2. 전역적으로 @Timeout 비활성화
테스트 사례를 디버깅할 때 시간 초과는 테스트 실행 결과에 영향을 줄 수 있습니다.
예를 들어 테스트를 작성할 때 트랜잭션 관련 테스트를 완료하기 위해 제한 시간 값을 10초로 설정했지만 나중에는 20초로 증가했다고 가정합니다.
모든 테스트는 모든 어설션이 충족되더라도 테스트에 영향을 미치고 실패로 표시할 수 있습니다.
JUnit Jupiter는 제한 시간이 적용될 때 구성하기 위해 junit.jupiter.execution.timeout.mode 구성 매개변수를 지원합니다.
세 가지 모드가 있습니다.
1. enabled
2. disabled
3. disabled_on_debug
Example junit-platform.properties
junit.jupiter.execution.timeout.mode=disabled |
'Java > Junit 5' 카테고리의 다른 글
Junit 5 - 반복 테스트 (0) | 2023.03.13 |
---|---|
Junit 5 - 조건부 테스트 실행 (0) | 2023.03.09 |
Junit 5 - @Disabled (0) | 2023.03.09 |
Junit 5 - Assumptions (0) | 2023.03.08 |
Junit 5 – @TestInstance (0) | 2023.03.08 |