Tags:

Thực thi unit test có điều kiện JUnit 5

Trong bài viết này, chúng ta sẽ cùng tìm hiểu xem làm sao để một unit test thực thi khi nó thoả một điều kiện cho trước trong JUnit 5.

Operation System Conditions

Đôi khi chúng ta cần thay đổi các kịch bản thử nghiệm tuỳ thuộc vào hệ điều hành(HDH) mà chúng đang chạy.

Sử dụng @EnabledOnOs để cho phép một test method chỉ thực thi trên các HDH được chỉ định. Ví dụ cho 1 test method chỉ được chạy trên HDH Mac và Windows.

@Test
@EnabledOnOs({OS.MAC, OS.WINDOWS})
public void testRunOnWindowsAndMacOS() {
    // more code
}

Hoặc @DisabledOnOs để vô hiệu hoá unit test khi chúng chạy trên các hệ điều hành được chỉ định. Ví dụ vô hiệu hoá unit test khi chúng chạy trên HDH Linux.

@Test
@DisabledOnOs({OS.LINUX})
public void disabledOnLinux() {
    // more code
}

Java Runtime Environment Conditions

Chúng ta cũng có thể chỉ định các phiên bản JRE cho các unit test với @EnabledOnJre@DisableOnJre annotation. Chúng nhận tham số là một mảng các phiên bản JRE được chỉ định.

Ví dụ sử dụng @EnabledOnJre chỉ định test method chỉ được chạy trên JRE phiên bản 8 và 9.

@EnabledOnJre({JRE.JAVA_8, JRE.JAVA_9})
public void testRunOnJRE8_9() {
    // more code
}

Sử dụng @DisabledOnJre để vô hiệu hoá unit test trên phiên bản JRE 11.

@Test
@DisabledOnJre({JRE.JAVA_11})
public void disabledOnJRE11() {
    // more code
}

System Property Conditions

Giờ đây, chúng  ta cũng có thể chỉ đinh các unit test chạy dựa trên JVM system property với @EnabledIfSystemProperty annotation.

Để sử dụng chúng ta phải cung cấp 2 thuộc tính:

  • named – Chỉ định tên chính xác của system property.
  • matches – Chỉ định giá tri của system property so khớp theo regular expression (Ví dụ với java.vm.vendor, chỉ chúng ta có nhiều nhà cung cấp như Oracle, OpenJDK, etc.)

Chúng ta có thể xem giá thông số của system property với lệnh comandline

java -XshowSettings:properties -version

Hoặc code java

@Test
void printSystemProperties() {
    Properties properties = System.getProperties();
    properties.forEach((k, v) -> System.out.println(k + ":" + v));
}

Mình sử dụng MacOS, sử dụng JDK 8, chạy lệnh trên mình sẽ quan tâm đến giá trị sau dùng làm tham số @EnabledIfSystemProperty.

Note: Các bạn có thể chọn bất cứ thông số của của System chứ không bắt buộc phải giống như mình nha.

java.vm.vendor = Oracle Corporation

Tiến hành thử nghiệm với @EnabledIfSystemProperty cho phép test method chạy được trên máy tính mình xem sao.

// Full name 
@Test
@EnabledIfSystemProperty(named = "java.vm.vendor", matches = "Oracle Corporation")
public void testEnabledWithFullName() {
    // more code
}

// Su dung regex

@Test
@EnabledIfSystemProperty(named = "java.vm.vendor", matches = "Oracle.*")
public void testEnabledWithRegex() {
    // more code
}

Ngược lại sử dụng @DisabledIfSystemProperty để vô hiệu hoá test method các system cụ thể.

// Full name
@Test
@DisabledIfSystemProperty(named = "java.vm.vendor", matches = "Oracle Corporation")
public void testDisabledWithFullName() {
    // more code
}

// Su dung regex

@Test
@DisabledIfSystemProperty(named = "java.vm.vendor", matches = "Oracle.*")
public void testDisablededWithRegex() {
    // more code
}

Để cho phép hoặc vô hiệu hoá một unit test trên nhiều system khác nhau sử dụng @EnabledIfSystemProperties@DisabledIfSystemProperties.

@EnabledIfSystemProperties(value = {@EnabledIfSystemProperty(named = "java.vm.vendor", matches = "Oracle.*"),
@EnabledIfSystemProperty(named = "ava.vm.name", matches = ".*OpenJDK.*")})
public void testEnabledIfSystemProperties() {
}


@Test
@DisabledIfSystemProperties(value = {@DisabledIfSystemProperty(named = "java.vm.vendor", matches = "Oracle.*"),
        @DisabledIfSystemProperty(named = "ava.vm.name", matches = ".*OpenJDK.*")})
public void testDisabledIfSystemProperties() {
}

Environment Variable Conditions

@EnabledIfEnvironmentVariable và @DisabledIfEnvironmentVariable cho phép chúng ta chỉ định môi trường cụ thể mà các unit test có thể thực thi.

Cũng giống như System property condition, chúng cũng nhận 2 thông số

  • named – Chỉ định tên chính xác của environment variable.
  • matches – Chỉ định giá tri của environment variable property so khớp theo regular expression.
@Test
@EnabledIfEnvironmentVariable(named = "GDMSESSION", matches = "ubuntu")
public void onlyRunOnUbuntuServer() {
}

@Test
@DisabledIfEnvironmentVariable(named = "LC_TIME", matches = ".*UTF-8.")
public void shouldNotRunWhenTimeIsNotUTF8() {
}

Ví dụ lấy các thông số Environment variable của system.

@Test
void printEnvironmentProperties() {
    Map<String, String> env = System.getenv();
    env.forEach((k, v) -> System.out.println(k + ":" + v));
}

Custom conditions run unit test

JUnit 5 Jupiter còn cho phép chúng ta định nghĩa điều kiện để unit test thực thi với @EnabledIf@DisableIf annotation. Chúng nhận 3 tham số chính:

  • value – script tự định nghĩa.
  • engine(optional) – engine được sử dụng, mặc định là Oracle Nashorn.
  • reason(optional) – sử dụng cho mục đích ghi log khi unit test thực thi không thành công.

Để sử dụng @EnabledIf và @DisableIf chúng phải phải sử dụng junit-jupiter-api 5.1.0, các phiên bản trên thì nó không còn hỗ trợ nữa, thay vào đó nó sẽ thêm các annotation @EnabledIfSystemProperty, @EnabledIfEnvironmentVariable nhầm mục đích đơn giản hoá thay vì phải viết script.

Ví dụ sử dụng @EnableIf chỉ định unit test chỉ chạy trên máy Việt nam.

@Test
@EnabledIf("'VN' == systemProperty.get('user.country')")
public void onlyFrenchPeopleWillRunThisMethod() {
    //...
}

Một ví dụ về @DisabledIf

@Test
@DisabledIf("java.lang.System.getProperty('os.name').toLowerCase().contains('mac')")
public void shouldNotRunOnMacOS() {
    //...
}

Viết script với value có thể viết trên nhiều dòng tương tự như mã code những được đặt trong String, vì lí do này mà script rất khó để kiểm tra đúng hay sai.

@Test
@EnabledIf(value = {
    "load('nashorn:mozilla_compat.js')",
    "importPackage(java.time)",
    "",
    "var thisMonth = LocalDate.now().getMonth().name()",
    "var february = Month.FEBRUARY.name()",
    "thisMonth.equals(february)"
},
    engine = "nashorn",
    reason = "On {annotation}, with script: {script}, result is: {result}")
public void onlyRunsInFebruary() {
    //...
}

Trong script chúng ta có thể sử dụng một số biến được ràng buộc sẵn:

  • systemEnvironment – Truy cập các biến environment.
  • systemProperty – Truy cập các biến system.
@Test
@DisabledIf("systemEnvironment.get('XPC_SERVICE_NAME') != null" +
        "&& systemEnvironment.get('XPC_SERVICE_NAME').contains('intellij')")
public void notValidForIntelliJ() {
    //this method will not run on intelliJ
}

Custom annotation conditions

Nếu trong dự án có rất nhiều test method cùng điều kiện để thực thi thì chúng ta nên tạo riêng cho chúng một annotation để tránh trùng lặp code, tăng khả năng đọc hiểu và bảo trì dự án.

Ví dụ mình tạo một condition annotation không cho phép test method chạy trên HDH Windows, Solaris, và một số HDH khác không nằm trong danh sách của Junit cung cấp. Chỉ các JRE phiên bản 9, 10, 11 mới được thực thi test method

@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
@Test
@DisabledOnOs({OS.WINDOWS, OS.SOLARIS, OS.OTHER})
@EnabledOnJre({JRE.JAVA_9, JRE.JAVA_10, JRE.JAVA_11})
@interface ThisTestWillOnlyRunAtLinuxAndMacWithJava9Or10Or11 {
}
 
@ThisTestWillOnlyRunAtLinuxAndMacWithJava9Or10Or11
public void someSuperTestMethodHere() {
    // this method will run with Java9, 10, 11 and Linux or macOS.
}

Tóm lược

Unit test condition sẽ rất hữu ích trong các dự án được thiết kế được chạy trên các hệ điều hành, nền tảng và môi trường khác nhau. Chúng ta buộc phải có các điều kiện cho các unit test vì có thể trên những môi trường khác nhau mã nguồn sẽ cho ra các kết quả khác nhau.

Nếu các bạn gặp khó khăn trong quá trình thực nghiệm, có thể checkout source mình đã soạn sẵn tại github repository.

Nguồn tham khảo

https://www.baeldung.com/junit-5-conditional-test-execution

https://junit.org/junit5/docs/current/user-guide/#writing-tests-conditional-execution

https://mkyong.com/junit5/junit-5-conditional-test-examples/

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