본문 바로가기

Java/Junit 5

Junit 5 - timeouts

728x90

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(32));
        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(52));
        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(32));
            System.out.println("test_Add()");
        });
    }
 
    @Test
    void test_Multiply() throws Exception {
 
        // 실패하지만 프로세스를 완료합니다. 출력 및 실행 기간을 참조
        assertTimeout(Duration.ofSeconds(5), () -> {
            TimeUnit.SECONDS.sleep(6);
            assertEquals(5, Calculator.addition(32));
            System.out.println("test_Multiply()");
        });
    }
 
    @Test
    void test_assertTimeoutPreemptively() throws Exception {
 
        // 실패하지만 프로세스 중단, 출력 및 실행 기간 참조
        assertTimeoutPreemptively(Duration.ofSeconds(5), () -> {
            TimeUnit.SECONDS.sleep(6);
            assertEquals(5, Calculator.addition(32));
            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(32));
                System.out.println("Dynamic Test 1");
            }),
 
            dynamicTest("2nd dynamic test", () -> {
                TimeUnit.SECONDS.sleep(2);
                assertEquals(6, Calculator.multiplication(32));
                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(32));
                    System.out.println("Dynamic Test 3");
                });
            }),
    
            dynamicTest("4th dynamic test", () -> {
                assertTimeoutPreemptively(Duration.ofSeconds(5), () -> {
                    TimeUnit.SECONDS.sleep(10);
                    assertEquals(5, Calculator.addition(32));
                    System.out.println("Dynamic Test 4");
                });
            }));
    }
}
cs

 

테스트 수행 결과 입니다.

  1. 1st dynamic test – Passed – 3.006 s
  2. 2nd dynamic test – Passed – 2.004 s
  3. 3rd dynamic test – Failed – 10.004 s
  4. 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                                                                                                                         
                                                                                                                                                                                                          

 

728x90

'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