Tags:

Hướng dẫn sử dụng Extensions trong Junit 5

Trong bài biết này chúng ta sẽ cùng nhau tìm hiểu mô hình extensions được giới thiệu trong Junit 5. Cơ chế này cho phép chúng ta mở rộng hành vi của test-classestest-method.

Trong các phiên bản trước như Junit 4, cung cấp 2 cơ chế cho phép mở rộng là: test runnerrules. Để đơn giản hoá Junit 5 đã giới thiệu cơ chế mới thay thế được gọi là Extension API. Vậy Extension API có gì mới và cách sử dụng như thế nào, chúng ta sẽ tìm hiểu ở các phần tiếp theo. 

Junit 5 Extension Model

Junit 5 extensions là các hành vi mở rộng liên quan đến một sư kiện nhất định trong quá trình thực thi một unit-test.

Quá trình thực thi một unit-test sẽ trải qua các giai đoạn khác nhau, với mỗi giai đoạn chúng ta có thể đăng ký một extension và chúng sẽ được Junit engine gọi khi đạt đến giai đoạn này.

Tổng thể, có 5 kiểu extension chính:

  • test instance post-processing
  • conditional test execution
  • life-cycle callbacks
  • parameters resolution
  • exception handling

Chúng ta sẽ tìm hiểu lần lượt từng loại trên để xem khi nào cần và cách sử dụng chúng.

Maven dependency

Trước tiên chúng ta cần thêm dependency vào project maven. Thư việc chính chúng ta cần là junit-jupiter-engine.

<dependency>
    <groupId>org.junit.jupiter</groupId>
    <artifactId>junit-jupiter-engine</artifactId>
    <version>5.4.2</version>
    <scope>test</scope>
</dependency>

Tiếp theo, chúng ta sẽ cần đến 2 thư viện log4jh2database hỗ trợ cho các ví dụ ở phần sau:

<dependency>
    <groupId>org.apache.logging.log4j</groupId>
    <artifactId>log4j-core</artifactId>
    <version>2.8.2</version>
</dependency>
<dependency>
    <groupId>com.h2database</groupId>
    <artifactId>h2</artifactId>
    <version>1.4.196</version>
</dependency>

Các phiên bản mới nhất junit-jupiter-engineh2 và log4j-core có thể download tại Maven Central.

Tạo Junit 5 Extensions

Để khởi tạo một Junit 5 extension, chúng ta có thể định nghĩa một class implement từ một hoặc nhiều extension interface mà Junit 5 cung cấp. Tất cả các interface này đều thừa kế từ Extension marker interfce.

Test Instance Post Processor Extension

Extension loại này sẽ thực thi sau khi 1 instance của unit-test được khởi tạo. Để tạo ra test-instance-post-processor extension chúng ta cần implement TestInstancePostProcessor interface và override postProcessTestInstance() method.

Thông thường test-instance-post-processor được dùng để inject các dependency vào test instance. Ví dụ tạo một extension dùng để khởi tạo logger object.

package extensions.testinstancepostprocessor;

import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import org.junit.jupiter.api.extension.ExtensionContext;
import org.junit.jupiter.api.extension.TestInstancePostProcessor;

public class LoggingExtension implements TestInstancePostProcessor {
    @Override
    public void postProcessTestInstance(Object testInstance, ExtensionContext extensionContext) throws Exception {
        Logger logger = LogManager.getLogger(testInstance.getClass());
        testInstance.getClass()
                .getMethod("setLogger", Logger.class)
                .invoke(testInstance, logger);


    }
}

Chúng ta có thể thấy, postProcessTestInstance() method khởi tạo một logger object và tiến hành gọi setLogger() method để gán logger object vừa khởi tạo cho test instance sử dụng cơ chế reflection.

Để sử dụng LoggingExtension, chúng ta có thể đăng ký nó với test-class hoặc test-method với @ExtendWith annotation.

package extensions.testinstancepostprocessor;

import org.apache.logging.log4j.Logger;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.ExtendWith;
@ExtendWith(LoggingExtension.class)
public class LoggingExtensionTest {

    private Logger logger;

    @Test
    public void testLoggingExtension() {
        logger.error("testLoggingExtension");
    }

    public void setLogger(Logger logger) {
        this.logger = logger;
    }

}

Output: [main] ERROR extensions.testinstancepostprocessor.LoggingExtensionTest – testLoggingExtension

Note: Chúng ta phải định nghĩa setLogger() để LoggingExtension có thể sử dụng, nếu không chương trình sẽ ném ra NoSuchMethodException.

Conditional Test Execution

Junit 5 cung cấp một kiểu extension cho phép kiểm soát liệu 1 unit-test có thể được chạy hay không tuỳ thuộc vào điều kiện đã được triển khai trước đó với ExecutionCondition interface.

Ví dụ tạo EnvironmentExtension class triển khai ExecutionCondition và override evaluateExecutionCondition() method. EnvironmentExtension sẽ đọc cấu hình từ application.properties và kiểm tra, nếu env = qa thì unit-test sẽ bị bỏ qua.

package extensions.conditionaltest;

import org.junit.jupiter.api.extension.ConditionEvaluationResult;
import org.junit.jupiter.api.extension.ExecutionCondition;
import org.junit.jupiter.api.extension.ExtensionContext;

import java.io.FileReader;
import java.io.IOException;
import java.util.Properties;

public class EnvironmentExtension implements ExecutionCondition {

    @Override
    public ConditionEvaluationResult evaluateExecutionCondition(
            ExtensionContext context) {

        Properties props = new Properties();
        try {
            props.load(new FileReader("application.properties"));
        } catch (IOException e) {
            e.printStackTrace();
        }
        String env = props.getProperty("env");
        if ("qa".equalsIgnoreCase(env)) {
            return ConditionEvaluationResult
                    .disabled("Test disabled on QA environment");
        }

        return ConditionEvaluationResult.enabled(
                "Test enabled on QA environment");
    }
}

Để lược bỏ một test-class hoặc hay một test-method, chúng ta cần cấu hình env = qa và đặt EnvironmentExtension tương ứng.

// application.properties
env=qa

EnvironmentExtensionTest sẽ bị lược bỏ khi thực thi.

package extensions.conditionaltest;

import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.ExtendWith;

import static org.junit.jupiter.api.Assertions.assertTrue;

@ExtendWith(EnvironmentExtension.class)
public class EnvironmentExtensionTest {
    @Test
    public void testEnvironmentExtension() {
        assertTrue(true);
    }
}

Lifecycle callbacks

Lifecylcle callbacks là một tập các extension liên quan đến vòng đời của unit-test có thể được định nghĩa bằng cách implement các interface sau:

  • BeforeAllCallbackAfterAllCallback – thực thi trước và sau khi tất cả test-method thực thi xong.
  • BeforeEachCallBack AfterEachCallback – thực thi trước và sau mỗi test-method thực thi xong.
  • BeforeTestExecutionCallback AfterTestExecutionCallback – thực thi trước và sau test-method bắt đầu thực thi.

Nếu test-class cũng định nghĩa các method trong vòng đời của nó kết hợp với Lifecycle Callbacks thì thứ tự thực thi là:

  1. BeforeAllCallback
  2. BeforeAll
  3. BeforeEachCallback
  4. BeforeEach
  5. BeforeTestExecutionCallback
  6. Test
  7. AfterTestExecutionCallback
  8. AfterEach
  9. AfterEachCallback
  10. AfterAll
  11. AfterAllCallback

Nguồn tham khảo

https://www.baeldung.com/junit-5-extensions

0 0 votes
Article Rating
Subscribe
Notify of
guest
0 Comments
Inline Feedbacks
View all comments
0
Would love your thoughts, please comment.x
()
x