[跟着官方文档学JUnit5][三][WritingTests][学习笔记]
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));
}
}
输出结果
- 点赞
- 收藏
- 关注作者
评论(0)