[跟着官方文档学JUnit5][三][WritingTests][学习笔记]

举报
John2021 发表于 2022/04/23 20:06:40 2022/04/23
【摘要】 1.显示名字测试类和测试方法可以通过@DisplayName(带有空格、特殊字符甚至表情符号)声明自定义显示名称,这些名称将显示在测试报告中以及由测试运行程序和IDE显示。import org.junit.jupiter.api.DisplayName;import org.junit.jupiter.api.Test;@DisplayName("A special test case")...

1.显示名字

测试类和测试方法可以通过@DisplayName(带有空格、特殊字符甚至表情符号)声明自定义显示名称,这些名称将显示在测试报告中以及由测试运行程序和IDE显示。

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(){}
}

输出结果

1.1.显示名字生成器

JUnit Jupiter支持自定义显示名称生成器,这些生成器可以通过@DisplayNameGeneration注解进行配置。通过@DisplayName注解提供的值始终优先于DisplayNameGenerator生成的显示名称。
可以通过实现DisplayNameGenerator来创建生成器。以下是一些在Jupiter中的默认选项

显示名字生成器 行为
Standard 匹配自Jupiter5.0发布以来的标准显示名称生成行为
Simple 删除没有参数的方法的尾随括号
ReplaceUnderscores 将下划线替换为空格
IndicativeSentences 通过连接测试的名称和封闭的类来生成完整的句子

请注意,对于指示性事件,可以使用@IndicativeSentencesGeneration自定义分隔符,如下示例所示。

import org.junit.jupiter.api.*;
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) {
        }
    }
}

输出结果

1.2.设置默认显示名字生成器

可以使用junit.jupiter.displayname.generator.default配置参数来指定默认情况下要使用的DisplayNameGenerator的完全限定类名。就像通过@DisplayNameGeneration注解配置的显示名称生成器一样,提供的类必须实现DisplayNameGenerator接口。默认显示名称生成器将用于所有测试,除非封闭的测试类或测试界面上存在@DisplayNameGeneration注解。通过@DisplayName注解提供的值始终优先于DisplayNameGenerator生成的显示名称。
比如,若要默认使用ReplaceUnderscores显示名称生成器,应将配置参数设置为相应的完全限定类名(例如,在src/test/resources/junit-platform.properties中):junit.jupiter.displayname.generator.default=\org.junit.jupiter.api.DisplayNameGenerator$ReplaceUnderscores
同样,可以指定实现DisplayNameGenerator的任何自定义类的完全限定名称。总之,测试类或方法的显示名称是根据以下优先规则而定的:
1,@DisplayName注解的值(如果存在)
2,通过调用@DisplayNameGeneration注解中指定的DisplayNameGenerator(如果存在)
3,通过调用通过配置参数的默认DisplayNameGenerator(如果存在)
4,通过调用org.junit.jupiter.api.DisplayNameGenerator.Standard

2.断言

JUnit Jupiter附带了许多JUnit4具有的断言方法,并添加了一些非常适合与Java 8 lambdas一起使用的断言方法。所有JUnit Jupiter断言都是org.junit.jupiter.api.Assertions类中的静态方法。
例子:

public class Calculator {
    public int add(int number1, int number2) {
        return number1 + number2;
    }

    public int multiply(int number1, int number2) {
        return number1 * number2;
    }

    public int divide(int number1, int number2) {
        return number1 / number2;
    }
}
public class Person {
    String FirstName;
    String LastName;

    public Person(String firstName, String lastName) {
        FirstName = firstName;
        LastName = lastName;
    }

    public String getFirstName() {
        return FirstName;
    }

    public String getLastName() {
        return LastName;
    }

}
import static org.junit.jupiter.api.Assertions.*;

import com.example.domain.Person;
import com.example.util.Calculator;
import org.junit.jupiter.api.Test;

import java.time.Duration;
import java.util.concurrent.CountDownLatch;

class AssertionDemo {
    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 groupAssertions() {
        // 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(Duration.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(Duration.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(Duration.ofMinutes(2),AssertionDemo::greeting);
        assertEquals("Hello JUnit5",actualGreeting);
    }

    @Test
    void timeExceeded() {
        // The following assertion fails with an error message similar to:
        // execution exceeded timeout of 10 ms by 91 ms
        assertTimeout(Duration.ofMillis(10),()->{
            // Simulate task that takes more than 10 ms.
            Thread.sleep(100);
        });
    }

    @Test
    void timeoutExceededWithPreemptiveTermination() {
        assertTimeoutPreemptively(Duration.ofMillis(10),()->{
            // Simulate task that takes more than 10 ms.
            new CountDownLatch(1).await();
        });
    }

    private static String greeting() {
        return "Hello JUnit5";
    }
}

2.1.断言库

尽管JUnit Jupiter提供的断言工具满足许多测试场景的需求,但有时需要更多的额外功能,如匹配器。在这种情况下,JUnit团队建议使用第三方断言库,如AssertJ,Hamcrest,Truth等。因此,开发人员可以自由使用他们选择的断言库。
例如,匹配器和流畅API的组合可用于使断言更具描述性和可读性。但JUnit Jupiter的org.junit.jupiter.api.Assertions类不提供像JUnit4的org.junit.Assert类那样的assertThat()方法,该方法接受Hamcrest Matcher。相反,我们鼓励开发人员使用第三方断言库提供的对匹配器的内置支持。
下面的示例演示了如何在JUnit Jupiter测试中使用来自Hamcrest的assertThat()。只要Hamcrest库已添加,就可以静态导入assertThat(),is()和equalTo()等方法,然后在下面的assertWithHamcrestMatcher()方法等测试中使用它们。

import com.example.util.Calculator;
import org.junit.jupiter.api.Test;

import static org.hamcrest.CoreMatchers.*;
import static org.hamcrest.MatcherAssert.*;

class HamcrestAssertionsDemo {
    private final Calculator calculator = new Calculator();
    @Test
    void assertWithHamcrestMatcher() {
        assertThat(calculator.subtract(4, 1), is(equalTo(3)));
    }
}

当然,基于JUnit4编程模型的旧测试可以继续使用org.junit.Assert#assertThat

3.假设

JUnit Jupiter附带了JUnit4提供的假设方法的子集并添加了一些非常适合与Java 8 Lambda表达式和方法引用一起使用的假设方法。所有JUnit Jupiter假设都是org.junit.jupiter.api.Assumptions类中的静态方法。

import org.junit.jupiter.api.Test;

import static org.junit.jupiter.api.Assertions.*;
import static org.junit.jupiter.api.Assumptions.*;

class AssumptionsDemo {
    private final Calculator calculator = new Calculator();
    @Test
    void testOnlyOnCiServer() {
        assertTrue("CI".equals(System.getenv("ENV")));
        // remainder of test
    }

    @Test
    void testOnlyOnDeveloperWorkstation() {
        assertTrue("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.multiply(6, 7));
                });
        // perform these assertions in all environments
        assertEquals(42, calculator.multiply(6, 7));
    }
}

输出结果

【版权声明】本文为华为云社区用户原创内容,转载时必须标注文章的来源(华为云社区)、文章链接、文章作者等基本信息, 否则作者和本社区有权追究责任。如果您发现本社区中有涉嫌抄袭的内容,欢迎发送邮件进行举报,并提供相关证据,一经查实,本社区将立刻删除涉嫌侵权内容,举报邮箱: cloudbbs@huaweicloud.com
  • 点赞
  • 收藏
  • 关注作者

评论(0

0/1000
抱歉,系统识别当前为高风险访问,暂不支持该操作

全部回复

上滑加载中

设置昵称

在此一键设置昵称,即可参与社区互动!

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。