读书笔记 -- Junit 实战(3rd)Ch01 起步、Ch02 核心

Ch01 Junit 起步

1. 框架:

   是一个应用程序的半成品。提供一个可复用的公共结构,可以在多个应用程序间共享。

 

2. Junit 特性:

  • 针对每个单元测试,分离测试类实例和类加载器实例,以免产生副作用;
  • 使用 Junit 注解提供资源初始化和清理方法:@BeforeEach, @BeforeAll, @AfterEach, @AfterAll  (Junit 5 开始),以及 @Before, @BeforeClass, @After, @AfterClass  (Junit 4及以下版本);
  • 提供多种断言方法,使检查测试结果变得更容易;
  • 提供与 Maven 和 Gradle 等流行工具的集成,以及与 Eclipse、NetBeans、IntelliJ 等 IDE 的集成;

 

***  测试类:最好以 "Test" 结尾,IDE可以直接运行,

命令行 "mvn clean test" 或者 "mvn clean install" 将运行:类后缀为 "Test" + 方法有 @Test 注解的方法

相对来说,"mvn clean install" 比   "mvn clean test" 多生成了 jar 包(通过两者的命令导出到 log 进行比较)。

 

3. surefire 插件

1)没有 surefire 插件的运行结果

2)有 surefire 插件的结果

 


Ch02 Junit 核心

2.1 核心注解

几个重要概念:

1)测试类:可以是顶级类、静态成员类或使用 @Nested 注解的包含一个多或多个测试方法的内部类。

                    测试类不能是抽象的,必须有单一的构造方法。构造方法必须不带参数,或所带参数能通过依赖注入在运行时动态解析。

                    测试类允许是私有的(Junit5上,Junit 4 要求是公有类)。

2)测试方法:用 @Test、@RepeatedTest、@ParameterizedTest、@TestFactory、@TestTemplate 等注解的实例方法。测试方法不能是抽象的,返回值类型必须是 void。

3)生命周期方法:用 @BeforeAll、@AfterAll、@BeforeEach、@AfterEach 等注解的方法。

@BeforeAll  # 所有测试运行前运行一次,除非测试类用 @TestInstance(Lifecycle.PER_CLASS)注解
static void setUpClass() {}

@BeforeEach  # 每次测试运行前运行
void setUp() {}

@Test  # 运行测试用例
void testRegularWork() {}

@Test  # 运行测试用例
void testAdditionalWork() {}

@AfterEach  # 每次测试运行后运行
void tearDown() {}

@AfterAll    # 所有测试运行后只运行一次,除非测试类用 @TestInstance(Lifecycle.PER_CLASS)注解
static void tearDownClass() {}

 

@Test 注解的方法的注意事项:

  • JUnit 为每个@Test 注解的方法创建测试类的一个实例;
  • 测试结果与运行顺序无关(所以要求测试方法不能依赖);
  • 不能跨测试方法重用实例变量值;

 

其他注解:

注解 注解类型 目的
@DisplayName 
类、方法  declare a custom display name for the annotated test class or test method. 
 @Disabled 类、方法  signal that the annotated test class or test method is currently disabled and should not be executed. 
 @Nested 类 

signal that the annotated class is a nested, non-static test class (i.e., an inner class) that can share setup and state with an instance of its enclosing class 

主测试类和嵌套测试类紧密耦合。在嵌套测试类上注解 @Nested

     

在过滤测试用例的两个方式:

# 1. @tag 标签 在 类或方法上,然后通过 maven 命令

@Tag("individual")
public class CustomerTest {
    ...
}

> cd C:\Users\xxx\IdeaProjects\trunk\JunitInAction\ch02   // pom.xml 所在目录
> mvn clean test -Dgroups=individual

# 2. 通过插件 surefire
    <build>
        <plugins>
            <plugin>
                <groupId>org.apache.maven.plugins</groupId>
                <artifactId>maven-surefire-plugin</artifactId>
                <version>3.0.0</version>
                <configuration>-->
                    <groups>individual</groups>-->
                    <excludedGroups>repository</excludedGroups>-->
                </configuration>-->
            </plugin>
        </plugins>
    </build>

> cd C:\Users\xxx\IdeaProjects\trunk\JunitInAction\ch02   // pom.xml 所在目录
> mvn clean test

其他详细可参考: https://zhuanlan.zhihu.com/p/353017791?utm_id=0

 

2.4 断言

 assertAll:在 JUnit5 中,将检查所有的断言,即使一些失败。在 JUint4 中不是,其中一个断言失败,后面的将不会执行。

 assertTimeout:用于等待可运行对象完成:后面的可运行对象会执行完成,然后再判断超时多少

 assertTimeoutPreemptively:超时后停止运行可运行对象。超过了设置的 timeout,则后面的可运行对象将不会执行。

 

2.5 假设

 JUnit5 包含一组假设方法,适合与 Java8 的 Lambda 表达式一起使用。

@Test
void testNoJobToRun() {
    assumingThat(
            () -> environment.getJavaVersion().equals(EXPECTED_JAVA_VERSION),
            () -> assertFalse(systemUnderTest.hasJobToRun())
    );
}

 

2.6 JUnit5 的依赖注入

// 如果构造/普通 方法的参数是 TestInfo,那么 TestInfoParameterResolver 将提供该类型的一个实例。
// TestInfo 是一个类,其对象用于将当前运行的测试或容器的信息注入 @Test、@BeforeEach、@AfterEach、@BeforeAll、@AfterAll 等标注的方法中。
@Test
void testGetNameOfTheMethod(TestInfo testInfo) {
    assertEquals("testGetNameOfTheMethod(TestInfo)", testInfo.getDisplayName());
}

// 如果构造/普通 方法的参数是 TestReporter,那么 TestReporterParameterResolver 将提供该类型的一个实例。
// TestReporter 的参数可以注入 @Test、@BeforeEach、@AfterEach 等标注的方法中。
@Test
void testReportKeyValuePair(TestReporter testReporter) {
    testReporter.publishEntry("Key", "Value");
}

 

2.7 重复测试

public class RepeatedTestsTest {

    // 在Java开发中,有时我们需要在多个类之间共享数据,而静态变量是一种常用的方式。
    // 声明为 static,该变量可以在其他类中访问,并且在整个应用程序的生命周期内只会有一个实例。
    private static List<Integer> integerList = new ArrayList<>();

    @RepeatedTest(value = 5, name = "the list contains {currentRepetition} element(s), the set contains 1 element")
    // RepetitionInfo:可以记录当前的循环信息
    // TestReporter:将循环信息存入 key-value
    void testAddingToCollections(TestReporter testReporter, RepetitionInfo repetitionInfo) {
        integerList.add(repetitionInfo.getCurrentRepetition());
        // 打印对象地址,反映其是否每次都是创建一个新的对象
        System.out.println(System.identityHashCode(testReporter));

        testReporter.publishEntry("Repetition number", String.valueOf(repetitionInfo.getCurrentRepetition()));
        assertEquals(repetitionInfo.getCurrentRepetition(), integerList.size());
    }
}

 

2.8 参数化测试

// 1. @ParameterizedTest + @ValueSource,实现不同参数的参数化运行
@ParameterizedTest
@ValueSource(strings = {"Check three parameters", "Junit in Action"})
void testWordsInSentence(String sentence) {
    assertEquals(3, wordCounter.countWords(sentence));
}

// 2. @ParameterizedTest + @EnumSource,实现不同参数的参数化运行
@ParameterizedTest
@EnumSource(Sentences.class)
// 将 整个 Sentences.class 指定为枚举源,因此执行 3 次
void testWordsInSentence(Sentences sentences) {
    assertEquals(3, wordCounter.countWords(sentences.value()));
}

// 3. @ParameterizedTest + @CsvSource
@ParameterizedTest
@CsvSource({"2, Unit testing", "3, Junit in Action", "4, Write solid Java code"})
// csv 的每一行,将第一个值赋给 expected,第二个值赋给 sentence
void testWordsInSentence(int expected, String sentence) {
    assertEquals(expected, wordCounter.countWords(sentence));
}

// 4. @ParameterizedTest + @CsvFileSource
@ParameterizedTest
@CsvFileSource(resources = "/word_counter.csv")
void testWordsInSentence(int expected, String sentence) {
    assertEquals(expected, wordCounter.countWords(sentence));
}

 

2.9 动态测试

JUnit5 引入了新的动态编程模型,可以在运行时生成测试。必须用 @TestFactory 注解,其注解的方法不是常规测试,而是一个生成测试的工厂。

 

posted on 2023-10-13 21:04  bruce_he  阅读(51)  评论(0编辑  收藏  举报