在咱们的日常开发中,代码一边编码一边自测是常有的事,做好单元测验也是一名开发应该把握的技能,不说测验搞得多么强,至少会根本的,会功用测验,会功用测验。今日来学习下 单元测验。

1.JUnit5介绍

现在主要版本是 JUnit5,所以后面的内容也都是基于 JUnit5 做相关的介绍。JUnit5 是 JUnit 单元测验结构的严重晋级,需求运转在 Java8 以上的环境

JUnit5能够理解为是由三个不同而子项目构成:

  • 1.JUnit Platform,用于JVM上启动测验结构的根底服务,供给命令行,IDE和构建工具等办法履行测验的支撑。
  • 2.JUnit Jupiter,包括 JUnit 5 新的编程模型和扩展模型,主要便是用于编写测验代码和扩展代码。
  • 3.JUnit Vintage,用于在JUnit 5 中兼容运转 JUnit3.x 和 JUnit4.x 的测验用例

JUnit5目前的主要特性:

  • 供给全新的断语和测验注解,支撑测验类内嵌
  • 更丰厚的测验办法:支撑动态测验,重复测验,参数化测验等
  • 完成了模块化,让测验履行和测验发现等不同模块解耦,削减依靠
  • 供给对 Java 8 的支撑,如 Lambda 表达式,Sream API等。

2.测验环境

  • Java 21
  • Junit 5.10.0

3.Maven依靠

本篇学习内容涉及的依靠如下:

    <!-- 根底测验 -->
    <dependency>
        <groupId>org.junit.jupiter</groupId>
        <artifactId>junit-jupiter-api</artifactId>
        <version>5.10.0</version>
    </dependency>
    <dependency>
        <groupId>org.junit.jupiter</groupId>
        <artifactId>junit-jupiter-engine</artifactId>
        <version>5.10.0</version>
    </dependency>
    <!-- 带参测验 -->
    <dependency>
        <groupId>org.junit.jupiter</groupId>
        <artifactId>junit-jupiter-params</artifactId>
        <version>5.10.0</version>
    </dependency>

4.典型注解

@BeforeAll

润饰 static 办法,界说整个测验类在开端前的操作,比方一些初始化的操作。

@AfterAll

润饰 static 办法,界说整个测验类在完毕时的操作,比方一些整理作业。

@BeforeEach && @AfterEach

标注在每个测验用例办法,表明办法开端前或完毕时的履行,负责该测验用例所需求的的运转环境的准备和毁掉。

@Test

表明这个测验办法是一个测验用例。

由上面的这些注解,咱们能够做出如下流程来表明单元测验的完好进程:

Java Junit5 运用小结

@DisplayName

能够加在类上,也能够加在测验办法上,相当于一个显现名。

@Disable

相当于禁用当时的测验办法,测验时就会忽略该办法。

@RepeatedTest

表明该办法需求重复运转,详细几回,能够经过参数传入。

基于上面的介绍,咱们写一个简单的类测验:

package org.example;
import org.junit.jupiter.api.*;
import java.time.Duration;
@DisplayName("我的第一个测验用例")
public class MyFirstTestCaseTest {
    @BeforeAll
    public static void init() {
        System.out.println("初始化数据");
    }
    @AfterAll
    public static void cleanup() {
        System.out.println("整理数据");
    }
    @BeforeEach
    public void tearUp() {
        System.out.println("当时测验办法开端");
    }
    @AfterEach
    public void tearDown() {
        System.out.println("当时测验办法完毕");
    }
    @DisplayName("我的第一个测验")
    @Test
    public void testFirstTest() {
        System.out.println("我的第一个测验开端");
    }
    @DisplayName("我的第二个测验")
    @Test
    public void testSecondTest() {
        System.out.println("我的第二个测验开端");
    }
    @DisplayName("我的第三个测验")
    @Disabled
    @Test
    public void testThirdTest() {
        System.out.println("我的第三个测验开端测验");
    }
    @DisplayName("我的第四个测验-重复测验")
    @RepeatedTest(value = 3, name = "{displayName} 第 {currentRepetition} 次")
    public void repeatedTest() {
        System.out.println("正在履行重复测验");
    }
}    

5.断语

在测验办法中,咱们常常是给定一个预期的值和测验的结果值作比较,看对应的结果怎样,由此就由相关断语:

  • 断语相等,assertEqual
  • 断语不等,assertNotEqual
  • 多个断语,assertAll
  • 断语空或非空,assertNull/assertNotNull
  • 断语 true或false,assertTrue/assertFalse
  • 超时断语,assertTimeout/assertTimeoutPreemptively
  • 断语实例,assertInstanceOf
  • 断语反常,assertThrows

因为断语类包括内容许多,每个办法实际许多重载,这里仅选择几个重点说说,其他都是相似的。

demo

@DisplayName("我的第五个测验-单个断语")
@Test
public void testSingleAssertion() {
    Integer num = 1;
    Assertions.assertEquals(num, 1);
}
@DisplayName("我的第六个测验-多断语")
@Test
public void testGroupAssertions() {
    int[] nums = {0, 1, 2, 3, 4};
    Assertions.assertAll("nums",
            () -> Assertions.assertEquals(nums[0], 0),
            () -> Assertions.assertEquals(nums[1], 1),
            () -> Assertions.assertEquals(nums[2], 2),
            () -> Assertions.assertEquals(nums[3], 3),
            () -> Assertions.assertEquals(nums[4], 4)
        );
}
@DisplayName("我的第七个测验-超时操作")
@Test
public void testShouldCompleteInOneSecond() {
    // 无法做到时间的准确匹配
    Assertions.assertTimeoutPreemptively(Duration.ofSeconds(1), () -> Thread.sleep(999));
}
@DisplayName("我的第八个测验-反常测验")
@Test
public void testAssertThrowsException() {
    String str = null;
    // str 作为传入参数,会报非法传参反常,所以能够正常断语到
    Assertions.assertThrows(IllegalArgumentException.class, () -> {
        Integer.valueOf(str);
    });
}

6.带参数的测验办法

有的时分咱们需求一些带参数的测验办法,比方一次测验一个参数不行,那就来一组参数,或许有的传参需求多个参数怎么办。

这里咱们用到了 Junit5的 params 包供给功用,这也是上面咱们在依靠中添加的依靠项。

在有带参数的单元测验中主要介绍几个常用的,其他感兴趣能够看看源码。

@ParameterizedTest

此处能够用来代替 @Test 注解,相同的成效。

@ValueSource

指定咱们的传入的这一组参数,能够是:

  • ints
  • strings
  • classes等

包括类型掩盖根本类型。

@CsvSource

规则了传入的多个入参的组合方式,默认用“,”分隔。

下面是个简单的 demo:

package org.example;
import org.junit.jupiter.api.Assertions;
import org.junit.jupiter.params.ParameterizedTest;
import org.junit.jupiter.params.provider.CsvSource;
import org.junit.jupiter.params.provider.ValueSource;
public class ParameterUnitTest {
    @ParameterizedTest
    @ValueSource(ints = {2, 4, 8, 10})
    public void testIsEvenNumber(int num) {
        Assertions.assertEquals(0, num % 2);
    }
    @ParameterizedTest
    @ValueSource(strings = {"Effective Java", "C Plus Plus"})
    public void testPrintTitle(String title) {
        System.out.println(title);
    }
    // 多参数
    @ParameterizedTest
    @CsvSource({"1,One", "2,Two"})
    public void testDataFromCSV(long id, String name) {
        System.out.printf("id: %d, name: %sn", id, name);
    }
}

以上这些都是一些根本用法,有的时分咱们需求依靠其他类,所以就会有 mock打桩 的需求,这是后话了,把握这些根本用法能掩盖相当的开发自测了。

参阅: