JUnit5编写基本测试
JUnit5的测试不是通过名称,而是通过注解来标识的。
测试类与方法
Test Class:测试类,必须包含至少一个test方法,包括:
- 最外层的class
 staticmember class@Nestedclass
Test Method:测试方法,包括:
@Test@RepeatedTest@ParameterizedTest@TestFactory@TestTemplate
Lifecycle Method:生命周期方法,包括:
@BeforeAll@AfterAll@BeforeEach@AfterEach
注意:
- Test Method和Lifecycle Method不能是
abstract,也不能return。它们可以在当前测试类中声明,也可以继承自父类或接口。 - Test class、Test Method和Lifecycle Method都不能是private。
 
示例代码:
import static org.junit.jupiter.api.Assertions.fail;
import static org.junit.jupiter.api.Assumptions.assumeTrue;
import org.junit.jupiter.api.AfterAll;
import org.junit.jupiter.api.AfterEach;
import org.junit.jupiter.api.BeforeAll;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Disabled;
import org.junit.jupiter.api.Test;
class StandardTests {
    @BeforeAll
    static void initAll() {
    }
    @BeforeEach
    void init() {
    }
    @Test
    void succeedingTest() {
    }
    @Test
    void failingTest() {
        fail("a failing test");
    }
    @Test
    @Disabled("for demonstration purposes")
    void skippedTest() {
        // not executed
    }
    @Test
    void abortedTest() {
        assumeTrue("abc".contains("Z"));
        fail("test should have been aborted");
    }
    @AfterEach
    void tearDown() {
    }
    @AfterAll
    static void tearDownAll() {
    }
}
自定义显示名字
Test class和test method可以使用@DisplayName自定义在测试报告中的显示名字,支持空格、特殊字符和emoji表情符号。
示例:
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() {
    }
}
除了@DisplayName,@DisplayNameGeneration 注解能用来对显示名字做统一处理,JUnit Jupiter自带了一些生成器:
- Standard 匹配标准行为
 - Simple 删除没有参数的方法后面的括号
 - ReplaceUnderscores 用空格替换下划线
 - IndicativeSentences 把test class和test method名字连接起来
 
示例代码:
import org.junit.jupiter.api.DisplayName;
import org.junit.jupiter.api.DisplayNameGeneration;
import org.junit.jupiter.api.DisplayNameGenerator;
import org.junit.jupiter.api.IndicativeSentencesGeneration;
import org.junit.jupiter.api.Nested;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.params.ParameterizedTest;
import org.junit.jupiter.params.provider.ValueSource;
class DisplayNameGeneratorDemo {
    @Nested
    @DisplayNameGeneration(DisplayNameGenerator.ReplaceUnderscores.class)
    class A_year_is_not_supported {
        @Test
        void if_it_is_zero() {
        }
        @DisplayName("A negative value for year is not supported by the leap year computation.")
        @ParameterizedTest(name = "For example, year {0} is not supported.")
        @ValueSource(ints = { -1, -4 })
        void if_it_is_negative(int year) {
        }
    }
    @Nested
    @IndicativeSentencesGeneration(separator = " -> ", generator = DisplayNameGenerator.ReplaceUnderscores.class)
    class A_year_is_a_leap_year {
        @Test
        void if_it_is_divisible_by_4_but_not_by_100() {
        }
        @ParameterizedTest(name = "Year {0} is a leap year.")
        @ValueSource(ints = { 2016, 2020, 2048 })
        void if_it_is_one_of_the_following_years(int year) {
        }
    }
}
@IndicativeSentencesGeneration可以自定义separator和generator。
结果:
+-- DisplayNameGeneratorDemo [OK]
  +-- A year is not supported [OK]
  | +-- A negative value for year is not supported by the leap year computation. [OK]
  | | +-- For example, year -1 is not supported. [OK]
  | | '-- For example, year -4 is not supported. [OK]
  | '-- if it is zero() [OK]
  '-- A year is a leap year [OK]
    +-- A year is a leap year -> if it is divisible by 4 but not by 100. [OK]
    '-- A year is a leap year -> if it is one of the following years. [OK]
      +-- Year 2016 is a leap year. [OK]
      +-- Year 2020 is a leap year. [OK]
      '-- Year 2048 is a leap year. [OK]
除了注解,也能通过配置设定全局的默认Generator,比如在 src/test/resources/junit-platform.properties文件中:
junit.jupiter.displayname.generator.default = \
    org.junit.jupiter.api.DisplayNameGenerator$ReplaceUnderscores
既可以指定现有Generator,也可以指定实现了DisplayNameGenerator接口的类。
自定义显示名字的优先级是:
@DisplayName@DisplayNameGenerationjunit.jupiter.displayname.generator.defaultorg.junit.jupiter.api.DisplayNameGenerator.Standard
断言(Assertions)
JUnit5的断言是包含在org.junit.jupiter.api.Assertions中的静态方法,比如assertTrue、assertEquals、assertNotNull、assertAll、assertThrows、assertTimeout、assertTimeoutPreemptively等。
示例代码如下:
import static java.time.Duration.ofMillis;
import static java.time.Duration.ofMinutes;
import static org.junit.jupiter.api.Assertions.assertAll;
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertNotNull;
import static org.junit.jupiter.api.Assertions.assertThrows;
import static org.junit.jupiter.api.Assertions.assertTimeout;
import static org.junit.jupiter.api.Assertions.assertTimeoutPreemptively;
import static org.junit.jupiter.api.Assertions.assertTrue;
import java.util.concurrent.CountDownLatch;
import example.domain.Person;
import example.util.Calculator;
import org.junit.jupiter.api.Test;
class AssertionsDemo {
    private final Calculator calculator = new Calculator();
    private final Person person = new Person("Jane", "Doe");
    @Test
    void standardAssertions() {
        assertEquals(2, calculator.add(1, 1));
        assertEquals(4, calculator.multiply(2, 2),
                "The optional failure message is now the last parameter");
        assertTrue('a' < 'b', () -> "Assertion messages can be lazily evaluated -- "
                + "to avoid constructing complex messages unnecessarily.");
    }
    @Test
    void groupedAssertions() {
        // In a grouped assertion all assertions are executed, and all
        // failures will be reported together.
        assertAll("person",
            () -> assertEquals("Jane", person.getFirstName()),
            () -> assertEquals("Doe", person.getLastName())
        );
    }
    @Test
    void dependentAssertions() {
        // Within a code block, if an assertion fails the
        // subsequent code in the same block will be skipped.
        assertAll("properties",
            () -> {
                String firstName = person.getFirstName();
                assertNotNull(firstName);
                // Executed only if the previous assertion is valid.
                assertAll("first name",
                    () -> assertTrue(firstName.startsWith("J")),
                    () -> assertTrue(firstName.endsWith("e"))
                );
            },
            () -> {
                // Grouped assertion, so processed independently
                // of results of first name assertions.
                String lastName = person.getLastName();
                assertNotNull(lastName);
                // Executed only if the previous assertion is valid.
                assertAll("last name",
                    () -> assertTrue(lastName.startsWith("D")),
                    () -> assertTrue(lastName.endsWith("e"))
                );
            }
        );
    }
    @Test
    void exceptionTesting() {
        Exception exception = assertThrows(ArithmeticException.class, () ->
            calculator.divide(1, 0));
        assertEquals("/ by zero", exception.getMessage());
    }
    @Test
    void timeoutNotExceeded() {
        // The following assertion succeeds.
        assertTimeout(ofMinutes(2), () -> {
            // Perform task that takes less than 2 minutes.
        });
    }
    @Test
    void timeoutNotExceededWithResult() {
        // The following assertion succeeds, and returns the supplied object.
        String actualResult = assertTimeout(ofMinutes(2), () -> {
            return "a result";
        });
        assertEquals("a result", actualResult);
    }
    @Test
    void timeoutNotExceededWithMethod() {
        // The following assertion invokes a method reference and returns an object.
        String actualGreeting = assertTimeout(ofMinutes(2), AssertionsDemo::greeting);
        assertEquals("Hello, World!", actualGreeting);
    }
    @Test
    void timeoutExceeded() {
        // The following assertion fails with an error message similar to:
        // execution exceeded timeout of 10 ms by 91 ms
        assertTimeout(ofMillis(10), () -> {
            // Simulate task that takes more than 10 ms.
            Thread.sleep(100);
        });
    }
    @Test
    void timeoutExceededWithPreemptiveTermination() {
        // The following assertion fails with an error message similar to:
        // execution timed out after 10 ms
        assertTimeoutPreemptively(ofMillis(10), () -> {
            // Simulate task that takes more than 10 ms.
            new CountDownLatch(1).await();
        });
    }
    private static String greeting() {
        return "Hello, World!";
    }
}
假设(Assumptions)
JUnit5的断言是包含在org.junit.jupiter.api.Assumptions中的静态方法,比如assumeTrue、assumingThat等。
示例代码:
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assumptions.assumeTrue;
import static org.junit.jupiter.api.Assumptions.assumingThat;
import example.util.Calculator;
import org.junit.jupiter.api.Test;
class AssumptionsDemo {
    private final Calculator calculator = new Calculator();
    @Test
    void testOnlyOnCiServer() {
        assumeTrue("CI".equals(System.getenv("ENV")));
        // remainder of test
    }
    @Test
    void testOnlyOnDeveloperWorkstation() {
        assumeTrue("DEV".equals(System.getenv("ENV")),
            () -> "Aborting test: not on developer workstation");
        // remainder of test
    }
    @Test
    void testInAllEnvironments() {
        assumingThat("CI".equals(System.getenv("ENV")),
            () -> {
                // perform these assertions only on the CI server
                assertEquals(2, calculator.divide(4, 2));
            });
        // perform these assertions in all environments
        assertEquals(42, calculator.multiply(6, 7));
    }
}
Assertions与Assumptions区别
Assertions如果失败,test会被标记为failed。Assumptions如果失败,test会被标记为ignored,测试不会执行。
示例:
import org.junit.jupiter.api.Test;
import static org.junit.jupiter.api.Assertions.assertTrue;
import static org.junit.jupiter.api.Assumptions.assumeTrue;
public class Demo {
    @Test
    void assertTest() {
        assertTrue(false);
    }
    @Test
    void assumeTest() {
        assumeTrue(false);
    }
}
结果:

禁用测试
@Disabled能用来禁用test class或test method,建议在括号内填写上禁用理由。
示例:
import org.junit.jupiter.api.Disabled;
import org.junit.jupiter.api.Test;
@Disabled("Disabled until bug #99 has been fixed")
class DisabledClassDemo {
    @Test
    void testWillBeSkipped() {
    }
}
import org.junit.jupiter.api.Disabled;
import org.junit.jupiter.api.Test;
class DisabledTestsDemo {
    @Disabled("Disabled until bug #42 has been resolved")
    @Test
    void testWillBeSkipped() {
    }
    @Test
    void testWillBeExecuted() {
    }
}
小结
本文首先介绍了如何使用测试类与方法,来编写一个JUnit5的基本测试,然后介绍了如何自定义测试报告中的显示名字。使用断言(Assertions)可以把test标记为failed,使用假设(Assumptions)可以把test标记为ignored。最后介绍了如何禁用测试。除了基本测试,JUnit5还能编写带条件的测试。
参考资料:
https://junit.org/junit5/docs/current/user-guide/#writing-tests
https://stackoverflow.com/questions/44628483/assume-vs-assert-in-junit-tests
JUnit5编写基本测试的更多相关文章
- 新书《编写可测试的JavaScript代码 》出版,感谢支持
		
本书介绍 JavaScript专业开发人员必须具备的一个技能是能够编写可测试的代码.不管是创建新应用程序,还是重写遗留代码,本书都将向你展示如何为客户端和服务器编写和维护可测试的JavaScript代 ...
 - 编写可测试的JavaScript代码
		
<编写可测试的JavaScript代码>基本信息作者: [美] Mark Ethan Trostler 托斯勒 著 译者: 徐涛出版社:人民邮电出版社ISBN:9787115373373上 ...
 - 使用FsCheck编写Property-based测试
		
使用FsCheck编写Property-based的测试 在编写基于Property-based的单元测试一文中,我们介绍了什么是Property-based测试.同时我们也总结了Property-b ...
 - 编写Avocado测试
		
编写Avocado测试 现在我们开始使用python编写Avocado测试,测试继承于avocado.Test. 基本例子 创建一个时间测试,sleeptest,测试非常简单,只是sleep一会: i ...
 - springboot快速入门02--Controller编写和测试
		
02springboot快速入门--Controller编写和测试 1.新建一个HelloController import org.springframework.boot.SpringApplic ...
 - Shell脚本的编写及测试
		
Shell脚本的编写及测试 1.1问题 本例要求两个简单的Shell脚本程序,任务目标如下: 编写一 ...
 - 098 01 Android 零基础入门  02 Java面向对象 03 综合案例(学生信息管理) 02 案例分析及实现 02 编写并测试Subject类
		
098 01 Android 零基础入门 02 Java面向对象 03 综合案例(学生信息管理) 02 案例分析及实现 02 编写并测试Subject类 本文知识点:编写并测试Subject类 说明: ...
 - 099 01 Android 零基础入门  02 Java面向对象 03 综合案例(学生信息管理) 02 案例分析及实现 03 编写并测试Student类
		
099 01 Android 零基础入门 02 Java面向对象 03 综合案例(学生信息管理) 02 案例分析及实现 03 编写并测试Student类 本文知识点:编写并测试Subject类 说明: ...
 - JUnit5的条件测试、嵌套测试、重复测试
		
条件测试 JUnit5支持条件注解,根据布尔值判断是否执行测试. 自定义条件 @EnabledIf和@DisabledIf注解用来设置自定义条件,示例: @Test @EnabledIf(" ...
 
随机推荐
- 【转载】基于Linux命令行KVM虚拟机的安装配置与基本使用
			
基于Linux命令行KVM虚拟机的安装配置与基本使用 https://alex0227.github.io/2018/06/06/%E5%9F%BA%E4%BA%8ELinux%E5%91%BD%E4 ...
 - 021.Python的内置函数
			
内置函数 1 abs 绝对值函数 res = abs(-9.9867) print(res) 执行 [root@node10 python]# python3 test.py 9.9867 2 rou ...
 - 面试侃集合 | SynchronousQueue公平模式篇
			
面试官:呦,小伙子来的挺早啊! Hydra:那是,不能让您等太久了啊(别废话了快开始吧,还赶着去下一场呢). 面试官:前面两轮表现还不错,那我们今天继续说说队列中的SynchronousQueue吧. ...
 - typeof的作用及用法
			
typeof的作用及用法 1.检查一个变量是否存在,是否有值. typeof在两种情况下会返回"undefined":一个变量没有被声明的时候,和一个变量的值是undefined的 ...
 - TOF摄像机可以替代Flash激光雷达吗?
			
TOF摄像机可以替代Flash激光雷达吗? 一.基于ToF技术的Flash激光雷达 基本成像原理上ToF Camera与LiDAR相同,都采用飞行时间测距技术(包括利用APD或SPAD的直接测距法,和 ...
 - LLVM编译器架构
			
LLVM编译器架构 LLVM概述 LLVM项目是模块化和可重用的编译器及工具链技术的集合.尽管名称如此,LLVM与传统虚拟机关系不大.名称" LLVM"本身不是缩写.它是项目的全名 ...
 - Solon Auth 认证框架使用演示(更简单的认证框架)
			
最近看了好几个认证框架,什么 Appache Shiro 啦.Sa-Token 啦.Spring Security啦...尤其是Spring Security,做为对标 Spring Boot &am ...
 - Python_Selenium 之以login_page为例实现对basepage封装好的方法调用和对common中公共方法的调用
			
目的:简化代码,提供框架该有的东西每一个函数 -提供了一个功能 - 公共的功能有了basepage,在PageObjects当中直接调用元素操作. 以下以login_page 为例,实现从配置文件中读 ...
 - Kali 2021.2 最新安装教程 图文详解(保姆式)
			
0x00 前言 Kali Linux 新版本(2021.2)增加大量新工具和功能,感兴趣的小伙伴可以到kali官网查看相关介绍. 新版采用Xfce 4.16桌面环境,附上帅照! 0x01 安装环境 宿 ...
 - 【题解】斐波拉契 luogu3938
			
题目 题目描述 小 C 养了一些很可爱的兔子. 有一天,小 C 突然发现兔子们都是严格按照伟大的数学家斐波那契提出的模型来进行 繁衍:一对兔子从出生后第二个月起,每个月刚开始的时候都会产下一对小兔子. ...