JUnit 5 가이드

1. 개요

JUnit은 Java 에코 시스템에서 가장 인기있는 단위 테스트 프레임 워크 중 하나입니다. JUnit 5 버전에는 Java 8 이상에서 새로운 기능을 지원 하고 다양한 스타일의 테스트를 가능하게 하는 것을 목표로하는 흥미로운 혁신이 많이 포함되어 있습니다 .

2. Maven 종속성

JUnit 5.x.0 설정은 매우 간단합니다. pom.xml에 다음 종속성을 추가해야합니다 .

 org.junit.jupiter junit-jupiter-engine 5.1.0 test 

이 버전 이 작동하려면 Java 8필요합니다 .

또한 IntelliJ뿐만 아니라 Eclipse의 JUnit 플랫폼에서 단위 테스트를 실행할 수있는 직접적인 지원이 있습니다. 물론 Maven Test 목표를 사용하여 테스트를 실행할 수도 있습니다.

반면 IntelliJ는 기본적으로 JUnit 5를 지원합니다. 따라서 IntelliJ에서 JUnit 5를 실행하는 것은 매우 간단합니다. 오른쪽 클릭 –> 실행 또는 Ctrl-Shift-F10 만 있으면됩니다.

3. 아키텍처

JUnit 5는 세 가지 하위 프로젝트의 여러 모듈로 구성됩니다.

3.1. JUnit 플랫폼

플랫폼은 JVM에서 테스트 프레임 워크를 시작하는 역할을합니다. JUnit과 빌드 도구와 같은 클라이언트 간의 안정적이고 강력한 인터페이스를 정의합니다.

최종 목표는 클라이언트가 테스트를 발견하고 실행할 때 JUnit과 쉽게 통합되는 방법입니다.

또한 JUnit 플랫폼에서 실행되는 테스트 프레임 워크를 개발하기위한 TestEngine API를 정의합니다. 이를 통해 사용자 정의 TestEngine을 구현하여 타사 테스트 라이브러리를 JUnit에 직접 플러그인 할 수 있습니다.

3.2. JUnit 목성

이 모듈에는 JUnit 5에서 테스트를 작성하기위한 새로운 프로그래밍 및 확장 모델이 포함되어 있습니다. JUnit 4와 비교 한 새로운 주석은 다음과 같습니다.

  • @TestFactory – 동적 테스트를위한 테스트 팩토리 인 메소드를 나타냅니다.
  • @DisplayName – 테스트 클래스 또는 테스트 메서드에 대한 사용자 지정 표시 이름을 정의합니다.
  • @Nested – 주석이 달린 클래스가 중첩 된 비 정적 테스트 클래스임을 나타냅니다.
  • @Tag – 테스트 필터링을위한 태그 선언
  • @ExtendWith – 사용자 지정 확장을 등록하는 데 사용됩니다.
  • @BeforeEach – 주석이 달린 메서드가 각 테스트 메서드 이전에 실행됨을 나타냅니다 (이전에는 @Before ).
  • @AfterEach – 각 테스트 메서드 (이전에는 @After ) 후에 주석이 추가 된 메서드가 실행됨을 나타냅니다.
  • @BeforeAll – 주석이 추가 된 메서드가 현재 클래스의 모든 테스트 메서드보다 먼저 실행됨을 나타냅니다 (이전 @BeforeClass ).
  • @AfterAll – 현재 클래스 (이전의 @AfterClass )의 모든 테스트 메서드 후에 주석이 추가 된 메서드가 실행됨을 나타냅니다.
  • @Disable – 테스트 클래스 또는 메서드를 비활성화하는 데 사용됩니다 (이전에는 @Ignore ).

3.3. JUnit 빈티지

JUnit 5 플랫폼에서 JUnit 3 및 JUnit 4 기반 테스트 실행을 지원합니다.

4. 기본 주석

새로운 주석에 대해 논의하기 위해 섹션을 실행을 담당하는 그룹으로 나누었습니다 : 테스트 전, 테스트 중 (선택 사항) 및 테스트 후 :

4.1. @BeforeAll@BeforeEach

다음은 주요 테스트 케이스 이전에 실행되는 간단한 코드의 예입니다.

@BeforeAll static void setup() { log.info("@BeforeAll - executes once before all test methods in this class"); } @BeforeEach void init() { log.info("@BeforeEach - executes before each test method in this class"); }

중요한 점은 @BeforeAll 주석 이있는 메서드 가 정적이어야한다는 것입니다. 그렇지 않으면 코드가 컴파일되지 않습니다.

4.2. @DisplayName@Disabled

새로운 테스트 옵션 방법으로 이동하겠습니다.

@DisplayName("Single test successful") @Test void testSingleSuccessTest() { log.info("Success"); } @Test @Disabled("Not implemented yet") void testShowSomething() { }

보시다시피 새 주석을 사용하여 표시 이름을 변경하거나 주석으로 메서드를 비활성화 할 수 있습니다.

4.3. @AfterEach@AfterAll

마지막으로 테스트 실행 후 작업에 연결된 메서드에 대해 살펴 보겠습니다.

@AfterEach void tearDown() { log.info("@AfterEach - executed after each test method."); } @AfterAll static void done() { log.info("@AfterAll - executed after all test methods."); }

@AfterAll 이있는 메서드 도 정적 메서드 여야합니다.

5. 주장과 가정

JUnit 5는 Java 8의 새로운 기능, 특히 람다 표현식을 최대한 활용하려고합니다.

5.1. 어설 션

어설 션이 org.junit.jupiter.api.Assertions 로 이동 되었으며 크게 개선되었습니다. 앞서 언급했듯이 이제 어설 션에서 람다를 사용할 수 있습니다.

@Test void lambdaExpressions() { assertTrue(Stream.of(1, 2, 3) .stream() .mapToInt(i -> i) .sum() > 5, () -> "Sum should be greater than 5"); }

위의 예는 사소한 것이지만 어설 션 메시지에 람다 식을 사용하는 한 가지 장점은 메시지 생성 비용이 많이 드는 경우 시간과 리소스를 절약 할 수있는 느리게 평가된다는 것입니다.

It is also now possible to group assertions with assertAll() which will report any failed assertions within the group with a MultipleFailuresError:

 @Test void groupAssertions() { int[] numbers = {0, 1, 2, 3, 4}; assertAll("numbers", () -> assertEquals(numbers[0], 1), () -> assertEquals(numbers[3], 3), () -> assertEquals(numbers[4], 1) ); }

This means it is now safer to make more complex assertions, as you will be able to pinpoint the exact location of any failure.

5.2. Assumptions

Assumptions are used to run tests only if certain conditions are met. This is typically used for external conditions that are required for the test to run properly, but which are not directly related to whatever is being tested.

You can declare an assumption with assumeTrue(), assumeFalse(), and assumingThat().

@Test void trueAssumption() { assumeTrue(5 > 1); assertEquals(5 + 2, 7); } @Test void falseAssumption() { assumeFalse(5  assertEquals(2 + 2, 4) ); }

If an assumption fails, a TestAbortedException is thrown and the test is simply skipped.

Assumptions also understand lambda expressions.

6. Exception Testing

There are two ways of exception testing in JUnit 5. Both of them can be implemented by using assertThrows() method:

@Test void shouldThrowException() { Throwable exception = assertThrows(UnsupportedOperationException.class, () -> { throw new UnsupportedOperationException("Not supported"); }); assertEquals(exception.getMessage(), "Not supported"); } @Test void assertThrowsException() { String str = null; assertThrows(IllegalArgumentException.class, () -> { Integer.valueOf(str); }); }

The first example is used to verify more detail of the thrown exception and the second one just validates the type of exception.

7. Test Suites

To continue the new features of JUnit 5, we will try to get to know the concept of aggregating multiple test classes in a test suite so that we can run those together. JUnit 5 provides two annotations: @SelectPackages and @SelectClasses to create test suites.

Keep in mind that at this early stage most IDEs do not support those features.

Let's have a look at the first one:

@RunWith(JUnitPlatform.class) @SelectPackages("com.baeldung") public class AllUnitTest {}

@SelectPackage is used to specify the names of packages to be selected when running a test suite. In our example, it will run all test. The second annotation, @SelectClasses, is used to specify the classes to be selected when running a test suite:

@RunWith(JUnitPlatform.class) @SelectClasses({AssertionTest.class, AssumptionTest.class, ExceptionTest.class}) public class AllUnitTest {}

For example, above class will create a suite contains three test classes. Please note that the classes don't have to be in one single package.

8. Dynamic Tests

The last topic that we want to introduce is JUnit 5 Dynamic Tests feature, which allows to declare and run test cases generated at run-time. In contrary to the Static Tests which defines fixed number of test cases at the compile time, the Dynamic Tests allow us to define the tests case dynamically in the runtime.

Dynamic tests can be generated by a factory method annotated with @TestFactory. Let's have a look at the code example:

@TestFactory public Stream translateDynamicTestsFromStream() { return in.stream() .map(word -> DynamicTest.dynamicTest("Test translate " + word, () -> { int id = in.indexOf(word); assertEquals(out.get(id), translate(word)); }) ); }

This example is very straightforward and easy to understand. We want to translate words using two ArrayList, named in and out, respectively. The factory method must return a Stream, Collection, Iterable, or Iterator. In our case, we choose Java 8 Stream.

Please note that @TestFactory methods must not be private or static. The number of tests is dynamic, and it depends on the ArrayList size.

9. Conclusion

이 글은 JUnit 5의 변경 사항에 대한 간략한 개요였습니다.

JUnit 5는 플랫폼 런처, 빌드 도구, IDE, 기타 단위 테스트 프레임 워크와의 통합 등과 관련된 아키텍처에 큰 변화가 있음을 알 수 있습니다. 또한 JUnit 5는 Java 8, 특히 Lambda 및 Stream 개념과 더 많이 통합됩니다. .

이 기사에 사용 된 예제는 GitHub 프로젝트에서 찾을 수 있습니다.