HomeOur Team
Java Unit Testing - Phần 3 - Một số Annotation cơ bản trong Junit
Solutions
Java Unit Testing - Phần 3 - Một số Annotation cơ bản trong Junit
hai.nguyen
hai.nguyen
March 22, 2021
4 min

Từ bản Junit4 lên Junit5 đã thay đổi khá nhiều về Annotation. Trong bài viết này sẽ đề cập đến các Annotation trong Junit5.

1. Một số Annotation cơ bản

  • @Test
  • @ParameterizedTest
  • @RepeatedTest
  • @TestFactory
  • @TestInstance
  • @TestTemplate
  • @DisplayName
  • @BeforeEach
  • @AfterEach
  • @BeforeAll
  • @AfterAll
  • @Nested
  • @Tag
  • @Disabled
  • @ExtendWith

1.1. @Test

Được sử dụng để đánh dấu method trong class hiện tại là method test. Trong bản Junit5, annotation này không định nghĩa bất cứ attribute nào vì test mở rộng trong JUnit Jupiter đã hoạt động dựa trên các annotation của riêng nó. Các phương thức như vậy được kế thừa trừ khi nó bị ghi đè. (Khác với Junit4 có 2 attribute là expectedtimeout).

1.2 @ParameterizedTest

Annotation này giúp co method test có thể thực thi test nhiều lần với các giá trị đầu vào khác nhau. Và chúng ta cần phải khai báo ít nhất một source có nhiệm vụ cung cấp các giá trị đầu vào cho mỗi lần thực thi test, và chúng cũng sẽ được sử dụng trong method test.

Ví dụ:

  • Sử dụng annotation @ValueSource kêt hợp với @ParameterizedTest để cung cấp một mảng String làm source:
@ParameterizedTest
@ValueSource(strings = { "racecar", "radar", "able was I ere I saw elba" })
void palindromes(String candidate) {
    assertTrue(StringUtils.isPalindrome(candidate));
}

Khi thực hiện method test trên, sẽ có 3 lần thực thi test với với 3 giá trị đầu vào khác được khai báo ở annotation @ValueSource. Và mỗi lần thực thi test sẽ được report kết quả riêng biệt:

palindromes(String) ✔
├─ [1] candidate=racecar ✔
├─ [2] candidate=radar ✔
└─ [3] candidate=able was I ere I saw elba ✔

Tương tự, annotation @ParameterizedTest có thể kết hợp cùng với các annotation cung cấp source khác.

1.3 @RepeatedTest

Annotation này cho phép method test có thể thực hiện lặp lại theo số lần được chỉ định. Mỗi lần lặp lại là một lần thực thi method test như bình thường.

Ví dụ: Với annotation @RepeatedTest(10) method repeatedTest sẽ được thực thi 10 lần

@RepeatedTest(10)
void repeatedTest() {
    // ...
}

1.4 @TestFactory

Annotation để chỉ định method test là một DynamicTest. Tức là một unit test được tạo ra tại thời điểm runtime (trái ngược với method test thông tường (với annotation @Test), các unit test được tạo ra tại thời điểm biên dịch).

@TestFactory method phải trả về một Stream, Collection, Iterable hoặc Iterator của DynamicTest instance. Một điểm lưu ý là @DynamicTest không hỗ trợ LifeCycle callbacks. Có nghĩa là @BeforeEach@AfterEach method sẽ không được gọi trong DynamicTest.

1.5 @TestInstance

Annotation này dùng để cấu hình vòng đời cho một đối tượng test cụ thể. Ví dụ, nếu bạn muốn JUnit Jupiter thực hiện tất cả các phương thức test trên cùng một đối tượng test cụ thể, chỉ cần chú thích class test của bạn với @TestInstance (Lifecycle.PER_CLASS).

1.6 @TestTemplate

Nếu method test là @TestTemplate thì nó không phải là trường hợp test thông thường mà là một mẫu cho các unit test khác. Như vậy, nó được thiết kế để được gọi nhiều lần tùy thuộc vào số lượng ngữ cảnh yêu cầu được trả về bởi các provider đã đăng ký. Do đó, nó chỉ có thể được thực thi khi có ít nhất một TestTemplateInvocationContextProvider đã đăng ký.

Ví dụ bên dưới về các viết một test template và cách đăng ký, triển khai một TestTemplateInvocationContextProvider:

final List<String> fruits = Arrays.asList("apple", "banana", "lemon");

@TestTemplate
@ExtendWith(MyTestTemplateInvocationContextProvider.class)
void testTemplate(String fruit) {
    assertTrue(fruits.contains(fruit));
}

public class MyTestTemplateInvocationContextProvider
        implements TestTemplateInvocationContextProvider {

    @Override
    public boolean supportsTestTemplate(ExtensionContext context) {
        return true;
    }

    @Override
    public Stream<TestTemplateInvocationContext> provideTestTemplateInvocationContexts(
            ExtensionContext context) {

        return Stream.of(invocationContext("apple"), invocationContext("banana"));
    }

    private TestTemplateInvocationContext invocationContext(String parameter) {
        return new TestTemplateInvocationContext() {
            @Override
            public String getDisplayName(int invocationIndex) {
                return parameter;
            }

            @Override
            public List<Extension> getAdditionalExtensions() {
                return Collections.singletonList(new ParameterResolver() {
                    @Override
                    public boolean supportsParameter(ParameterContext parameterContext,
                            ExtensionContext extensionContext) {
                        return parameterContext.getParameter().getType().equals(String.class);
                    }

                    @Override
                    public Object resolveParameter(ParameterContext parameterContext,
                            ExtensionContext extensionContext) {
                        return parameter;
                    }
                });
            }
        };
    }
}

Output:

└─ testTemplate(String) ✔
   ├─ apple ✔
   └─ banana ✔

1.7 @DisplayName

Các test class và các test method có thể khai báo một tên với annotation @DisplayName. Tên có thể chứa dấu cách, ký tự đặc biệt kể cả biểu tượng. Tên được khai báo sẽ được hiển thị trong kết quả thực thi test.

Ví dụ:

import org.junit.jupiter.api.DisplayName;
import org.junit.jupiter.api.Test;

@DisplayName("A special test case")
class DisplayNameDemo {

    @Test
    @DisplayName("Custom test name containing spaces")
    void testWithDisplayNameContainingSpaces() {
    }

    @Test
    @DisplayName("╯°□°)╯")
    void testWithDisplayNameContainingSpecialCharacters() {
    }

    @Test
    @DisplayName("😱")
    void testWithDisplayNameContainingEmoji() {
    }
}

1.8 @BeforeEach

Method được gắn annotation này sẽ được thực thi trước mỗi method được gắn các annotation sau: @Test, @RepeatedTest, @ParameterizedTest, hoặc @TestFactory trong cùng một class. Annotation này tương tự như @Before trong JUnit4.

1.9 @AfterEach

Method được gắn annotation này sẽ được thực thi sau mỗi method được gắn các annotation sau: @Test, @RepeatedTest, @ParameterizedTest, hoặc @TestFactory trong cùng một class. Annotation này tương tự như @After trong JUnit4.

1.10 @BeforeAll

Method được gắn annotation này là static method và sẽ được thực thi trước tất cả method được gắn các annotation sau: @Test, @RepeatedTest, @ParameterizedTest, hoặc @TestFactory trong cùng một class. Annotation này tương tự như @BeforeClass trong JUnit4.

1.11 @AfterAll

Method được gắn annotation này là static method và sẽ được thực thi sau tất cả method được gắn các annotation sau: @Test, @RepeatedTest, @ParameterizedTest, hoặc @TestFactory trong cùng một class. Annotation này tương tự như @AfterClass trong JUnit4.

1.12 @Nested

Class được gắn annotation này là class thử nghiệm không lồng nhau, không phải là static class (non-static). method @BeforeAll và @AfterAll không thể được sử dụng trong class thử nghiệm @Nested trừ khi vòng đời cá thể kiểm tra “per-class” được sử dụng. Chú thích như vậy không được kế thừa.

1.13 @Tag

Chúng ta có thể phân loại và lọc các unit test với, nó có thể dùng với cả method và class.

import org.junit.jupiter.api.Tag;
import org.junit.jupiter.api.Test;

@Tag("tagClass")
public class TagExample {

    @Test
    @Tag("tagMethod")
    public void tagTest() {
        System.out.println("tagTest");
    }

    @Test
    public void normalTest() {
        System.out.println("normalTest");
    }
}

Khi chạy các unit test với Tag(“tagClass”) được đặt ở đầu class thì tất cả các method trong class đều được thực thi, bất kể nó được đánh dấu với một Tag khác. Khi chạy với Tag(“tagMethod”) thì chỉ có tagTest() method được thực thi.

Chúng ta có thể khởi tạo 1 test suite lọc theo Tag, khi đó test suite này chỉ thực hiện các unit test theo Tag được chỉ định trong test suite.

1.14 @Disabled

Được sử dụng để vô hiệu hoá một class hoặc một method, tương tự như annotation @Igrone trong JUnit4.

1.15 @ExtendWith

Annotation này được dùng để đăng ký các mở rộng tùy biến. Ví dụ để đăng ký một tùy biến RandomParametersExtension cho một method cụ thể, chúng ta chú thích method test như sau:

@ExtendWith(RandomParametersExtension.class)
@Test
void test(@Random int i) {
    // ...
}

2. LifeCycle của một test class trong JUnit

Chúng ta có thể có rất nhiều method trong JUnit test và mỗi test sẽ phải trải qua một vòng đời như ảnh dưới:

Trong ví dụ bên dưới, chúng ta sẽ thấy được cách mà một Unit test được thi thi với JUnit:

package com.gpcoder.junit;
 
import org.junit.After;
import org.junit.AfterClass;
import org.junit.Before;
import org.junit.BeforeClass;
import org.junit.Test;
 
public class JUnitLifeCycleTest {
 
    @BeforeClass
    public static void runOnceBeforeClass() {
        System.out.println("@BeforeClass - runOnceBeforeClass");
    }
 
    @AfterClass
    public static void runOnceAfterClass() {
        System.out.println("@AfterClass - runOnceAfterClass");
    }
 
    @Before
    public void runBeforeTestMethod() {
        System.out.println("@Before - runBeforeTestMethod");
    }
 
    @After
    public void runAfterTestMethod() {
        System.out.println("@After - runAfterTestMethod");
    }
 
    @Test
    public void test_method_1() {
        System.out.println("@Test - test_method_1");
    }
 
    @Test
    public void test_method_2() {
        System.out.println("@Test - test_method_2");
    }
}

Thứ tự thực thi các method trong một vòng đời được thể hiện qua output của ví dụ trên:

@BeforeClass - runOnceBeforeClass
@Before - runBeforeTestMethod
@Test - test_method_1
@After - runAfterTestMethod
@Before - runBeforeTestMethod
@Test - test_method_2
@After - runAfterTestMethod
@AfterClass - runOnceAfterClass

Trên đây là một số Annotation cơ bản và thường được sử dụng trong JUnit test.

Tham khảo: Junit document


hai.nguyen

hai.nguyen

Developer

Expertise

java

Related Posts

# Java Unit Testing - Phần 2 - Giới thiệu JUnit
March 24, 2021
4 min
© 2021, All Rights Reserved.

Quick Links

HomeOur Team

Social Media