什么是JUnit5

Junit是Java版的单元测试框架,单元测试也就是方法测试,从JUnit5开始支持Java8的语法。

本项目所有代码均在github开源:https://github.com/initit-com/JUnit5Demo

安装

推荐Maven引入

  1. <dependency>
  2. <groupId>org.junit.jupiter</groupId>
  3. <artifactId>junit-jupiter</artifactId>
  4. <version>RELEASE</version>
  5. <scope>test</scope>
  6. </dependency

Maven会自动引入三个jar,如下图

image.png

快速启动

@Test标注在一个void方法上,即一个单元测试

  1. /**
  2. * 方法必须是viod ,junit5以后可以不用public修饰
  3. */
  4. @Test
  5. void test() {
  6. //断言 2等于2
  7. assertEquals(2, 2);
  8. }

显示名称

@DisplayName(“指定显示测试名称”) 标注在方法上,指定测试的名称

  1. @Test
  2. @DisplayName("显示测试名称")
  3. void testWithDisplayNameContainingSpaces() {
  4. }
  5. @Test
  6. @DisplayName("特殊符号:【╯°□°)╯】 或表情: 😱")
  7. void testWithDisplayNameContainingSpecialCharacters() {
  8. }

显示结果如下
image.png

常用的断言assert

断言(预言)结果是否成立,成立则测试通过,否则报错提示

  1. @Test
  2. void standardAssertions() {
  3. //断言相等
  4. assertEquals(2, 2);
  5. //断言相等 并设置默认报错信息
  6. assertEquals(4, 4, "必须相等");
  7. //判断为true 并使用lambda表达式方式设置默认报错信息
  8. assertTrue(3 < 4, () -> "必须为true");
  9. }
  10. @Test
  11. void groupedAssertions() {
  12. // 断言一组
  13. assertAll("person",
  14. () -> assertEquals("Jane", "Jane"),
  15. () -> {
  16. System.out.println("这里模拟执行多行代码");
  17. assertAll(() -> {
  18. System.out.println("这里模拟多层深度测试");
  19. assertTrue(5 < 7);
  20. }
  21. );
  22. }
  23. );
  24. }
  25. //断言异常
  26. @Test
  27. void exceptionTesting() {
  28. Exception exception = assertThrows(RuntimeException.class, () -> {
  29. throw new RuntimeException("模拟抛出运行时异常");
  30. });
  31. assertEquals("模拟抛出运行时异常", exception.getMessage());
  32. }
  33. //断言执行时间
  34. @Test
  35. void timeoutNotExceeded() {
  36. //一秒内执行完毕
  37. assertTimeout(ofSeconds(1), () -> {
  38. System.out.println("执行");
  39. Thread.sleep(2000);
  40. });
  41. }
  42. //更多断言方式参考官方文档

假设assume

只有假设成立才执行后边的业务代码。

  1. @Test
  2. void testAssume() {
  3. assumeTrue("aa".equals("aa"));
  4. System.out.println("假设成功后才执行后边的代码,否则不执行");
  5. }
  6. //只有在windows系统下执行测试
  7. @Test
  8. void testOnlyWindows() {
  9. assumeTrue( System.getProperty("os.name").toLowerCase().contains("windows"));
  10. System.out.println("当前是windows系统时执行一下测试代码");
  11. //todo 测试代码
  12. }

禁用测试

  1. //标注在类上 该类的所有测试方法都被禁止执行
  2. @Disabled("禁止测试,因为#99bug已经修复")
  3. public class D005_禁止测试 {
  4. @Test
  5. void testWillBeSkipped() {
  6. }
  7. //标注在方法上 该方法禁止执行
  8. @Disabled("Disabled until bug #42 has been resolved")
  9. @Test
  10. void testWillBeSkipped2() {
  11. }
  12. }

测试的生命周期

配合Java8接口的静态方法/默认方法语法,我们实现如下功能:

  1. @TestInstance(TestInstance.Lifecycle.PER_CLASS)
  2. public interface D006_生命周期共用接口 {
  3. //默认方法default 也可以是 静态方法 static
  4. @BeforeAll
  5. default void beforeAllTests() {
  6. System.out.println("所有测试之前执行");
  7. }
  8. @AfterAll
  9. default void afterAllTests() {
  10. System.out.println("所有测试之后执行");
  11. }
  12. @BeforeEach
  13. default void beforeEachTest() {
  14. System.out.println("每个测试方法之前执行");
  15. }
  16. @AfterEach
  17. default void afterEachTest() {
  18. System.out.println("每个测试方法之后执行");
  19. }
  20. }

注意:生命周期需要用@TestInstance注解标注在测试类上,看下@TestInstance的源码

  1. @Target(ElementType.TYPE)
  2. @Retention(RetentionPolicy.RUNTIME)
  3. @Inherited
  4. @Documented
  5. @API(status = STABLE, since = "5.0")
  6. public @interface TestInstance {
  7. /**
  8. * Enumeration of test instance lifecycle <em>modes</em>.
  9. *
  10. * @see #PER_METHOD
  11. * @see #PER_CLASS
  12. */
  13. enum Lifecycle {
  14. /**
  15. * When using this mode, a new test instance will be created once per test class.
  16. *
  17. * @see #PER_METHOD
  18. */
  19. PER_CLASS,
  20. /**
  21. * When using this mode, a new test instance will be created for each test method,
  22. * test factory method, or test template method.
  23. *
  24. * <p>This mode is analogous to the behavior found in JUnit versions 1 through 4.
  25. *
  26. * @see #PER_CLASS
  27. */
  28. PER_METHOD;
  29. }
  30. /**
  31. * The test instance lifecycle <em>mode</em> to use.
  32. */
  33. Lifecycle value();
  34. }

写一个测试类实现上述接口

  1. public class D006_生命周期 implements D006_生命周期共用接口 {
  2. @Test
  3. void test1(){
  4. System.out.println("test1执行");
  5. }
  6. @Test
  7. void test2(){
  8. System.out.println("test2执行");
  9. }
  10. }

运行结果如下

image.png

重复执行测试

@RepeatedTest(执行次数),方法会执行n次

  1. @RepeatedTest(3)
  2. void repeatedTest() {
  3. System.out.println("该方法会重复执行3次");
  4. }

参数测试

  1. //参数值测试
  2. @ParameterizedTest
  3. @ValueSource(strings = {"red", "read", "rgb"})
  4. void test(String str) {
  5. assertTrue(str.contains("r"), "参数必须包含字母r");
  6. }
  7. //参数是枚举值测试
  8. @ParameterizedTest
  9. @EnumSource(TimeUnit.class)
  10. void testWithEnumSource(TimeUnit timeUnit) {
  11. assertNotNull(timeUnit);
  12. }
  13. //参数是方法
  14. @ParameterizedTest
  15. @MethodSource("stringProvider")
  16. void testWithExplicitLocalMethodSource(String argument) {
  17. assertNotNull(argument);
  18. }
  19. static Stream<String> stringProvider() {
  20. return Stream.of("apple", "banana");
  21. }
  22. //。。。等等其他参数值类型测试

参数值类型主要在包org.junit.jupiter.params.provider下,如下图所示:

image.png

超时测试

  1. @Test
  2. @Timeout(2) //默认是秒 可以修改
  3. void test() {
  4. //超过2秒报错
  5. }
  6. //@Timeout源码如下,默认为秒,可以更改为毫秒
  7. @Target({ ElementType.TYPE, ElementType.METHOD })
  8. @Retention(RetentionPolicy.RUNTIME)
  9. @Documented
  10. @Inherited
  11. @API(status = EXPERIMENTAL, since = "5.5")
  12. public @interface Timeout {
  13. /**
  14. * The duration of this timeout.
  15. *
  16. * @return timeout duration; must be a positive number
  17. */
  18. long value();
  19. /**
  20. * The time unit of this timeout.
  21. *
  22. * @return time unit
  23. * @see TimeUnit
  24. */
  25. TimeUnit unit() default TimeUnit.SECONDS;
  26. }

测试执行顺序

@Order(执行顺序的数字)表示执行顺序

@TestMethodOrder(MethodOrderer.OrderAnnotation.class)指定执行顺序

  1. @TestMethodOrder(MethodOrderer.OrderAnnotation.class)
  2. public class D010_测试执行顺序 {
  3. @Test
  4. @Order(2)
  5. void t2() {
  6. System.out.println(2);
  7. }
  8. @Test
  9. @Order(1)
  10. void t1() {
  11. System.out.println(1);
  12. }
  13. @Test
  14. @Order(3)
  15. void t3() {
  16. System.out.println(3);
  17. }
  18. }

常用注解

注释 描述
@Test 表示方法是单元测试方法,区别于JUnit4,可以不用public关键词修饰
@ParameterizedTest 表示方法为参数化测试
@RepeatedTest 表示方法是重复测试的测试模板
@TestFactory 表示方法是动态测试的测试工厂
@TestTemplate 表示方法是测试用例的模板,根据注册提供程序返回的调用上下文的数量,设计为多次调用。
@TestMethodOrder 用于配置已分号测试类的测试方法执行顺序;类似于 JUnit 4 的
@TestInstance 用于为测试类配置测试实例生命周期
@DisplayName 声明测试类或测试方法的自定义显示名称
@DisplayNameGeneration 声明测试类的自定义显示名称生成器
@BeforeEach 每个方法执行之前执行
@AfterEach 每个方法执行之后执行
@BeforeAll 所有方法执行之前执行
@AfterAll 所有方法执行之后执行
@Nested 表示注释类是非静态嵌套测试类
@Tag 用于在类或方法级别声明用于筛选测试的标记;类似于 JUnit 4 中的 TestNG 或类别中的测试组
@Disabled 用于禁用测试类或测试方法;类似于 JUnit 4 的 @Ignore
@Timeout 标注超时测试
@ExtendWith 用于声明性地注册扩展
@RegisterExtension 用于通过字段以编程方式注册扩展
@TempDir 用于在生命周期方法或测试方法中通过字段注入或参数注入提供临时目录;位于包中。org.junit.jupiter.api.io

参考资料