Junit5
JUnit 5 = JUnit Platform + JUnit Jupiter + JUnit Vintage
JUnit Jupiter支持配置测试和扩展框架的以下注解。
所有核心注解都位于 模块的org.junit.jupiter.api包中junit-jupiter-api。
注解 | 描述 |
---|---|
@Test | 表示一种方法是一种测试方法。与JUnit 4的@Test注解不同,此注解并未声明任何属性,因为JUnit Jupiter中的测试扩展基于其专用注解进行操作。除非这些方法被覆盖,否则这些方法会被继承。 |
@ParameterizedTest | 表示一种方法是参数化测试。除非这些方法被覆盖,否则这些方法会被继承。 |
@RepeatedTest | 表示方法是重复测试的测试模板。除非这些方法被覆盖,否则这些方法会被继承。 |
@TestFactory | 表示一种方法是动态测试的测试工厂。除非这些方法被覆盖,否则这些方法会被继承。 |
@TestInstance | 用于配置注解测试类的测试实例生命周期。这些注解是继承的。 |
@TestTemplate | 表示方法是测试用例的模板,设计为根据注册提供程序返回的调用上下文的数量调用多次。除非这些方法被覆盖,否则这些方法会被继承。 |
@DisplayName | 声明测试类或测试方法的自定义显示名称。这些注解不会被继承。 |
@BeforeEach | 表示该注解的方法应该被执行之前 的每个 @Test,@RepeatedTest,@ParameterizedTest,或@TestFactory方法在当前类; 类似于JUnit 4的@Before。除非这些方法被覆盖,否则这些方法会被继承。 |
@AfterEach | 表示该注解的方法应该被执行之后 每个 @Test,@RepeatedTest,@ParameterizedTest,或@TestFactory方法在当前类; 类似于JUnit 4的@After。除非这些方法被覆盖,否则这些方法会被继承。 |
@BeforeAll | 表示该注解的方法应该被执行之前 所有 @Test,@RepeatedTest,@ParameterizedTest,和@TestFactory方法在当前类; 类似于JUnit 4的@BeforeClass。这些方法是继承的(除非它们被隐藏或覆盖),并且必须是static(除非使用“每类” 测试实例生命周期)。 |
@AfterAll | 表示该注解的方法应该被执行之后 的所有 @Test,@RepeatedTest,@ParameterizedTest,和@TestFactory方法在当前类; 类似于JUnit 4的@AfterClass。这些方法是继承的(除非它们被隐藏或覆盖),并且必须是static(除非使用“每类” 测试实例生命周期)。 |
@Nested | 表示注解的类是嵌套的非静态测试类。@BeforeAll和@AfterAll方法不能直接在使用@Nested测试类除非“每级” 测试实例的生命周期被使用。这些注解不会被继承。 |
@Tag | 用于在类或方法级别声明过滤测试的标签 ; 类似于TestNG中的测试组或JUnit 4中的类别。这些注解在类级别上继承,但不在方法级别上继承。 |
@Disabled | 用于禁用测试类或测试方法; 类似于JUnit 4的@Ignore。这些注解不会被继承。 |
@ExtendWith | 用于注册自定义扩展。这些注解是继承的。 |
带注解的方法@Test,@TestTemplate,@RepeatedTest,@BeforeAll, @AfterAll,@BeforeEach,或@AfterEach注解不能返回一个值。
导包
<junit.version>5.2.0-M1</junit.version>
<dependency>
<groupId>org.junit.jupiter</groupId>
<artifactId>junit-jupiter-api</artifactId>
<version>${junit.version}</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.junit.jupiter</groupId>
<artifactId>junit-jupiter-engine</artifactId>
<version>${junit.version}</version>
<scope>test</scope>
</dependency>
如果需要源注解的话,需要加上
<dependency>
<groupId>org.junit.jupiter</groupId>
<artifactId>junit-jupiter-params</artifactId>
<version>${junit.version}</version>
<scope>test</scope>
</dependency>
标准测试类
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
import org.junit.jupiter.api.Tag;
@Target({ ElementType.TYPE, ElementType.METHOD })
@Retention(RetentionPolicy.RUNTIME)
@Tag("fast")
public @interface Fast {
}
import static org.junit.jupiter.api.Assertions.fail;
import org.junit.jupiter.api.AfterAll;
import org.junit.jupiter.api.AfterEach;
import org.junit.jupiter.api.BeforeAll;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Disabled;
import org.junit.jupiter.api.Test;
class StandardTests {
@BeforeAll
static void initAll() {
}
@BeforeEach
void init() {
}
@Test
void succeedingTest() {
}
@Test
void failingTest() {
fail("a failing test");
}
@Test
@Disabled("for demonstration purposes")
void skippedTest() {
// not executed
}
@AfterEach
void tearDown() {
}
@AfterAll
static void tearDownAll() {
}
}
测试类和测试方法都不需要public
断言
依旧可以使用断言assertThat
、 is
、 equalTo
、 assumeTrue
、 assertAll
、assertEquals
等
假设
JUnit Jupiter附带了JUnit 4提供的一些假设方法的子集,并增加了一些适合与Java 8 lambda一起使用的假设方法。所有JUnit Jupiter假设都是org.junit.jupiter.api.Assumptions类中的静态方法。
@Test
void testOnlyOnCiServer() {
assumeTrue("CI".equals(System.getenv("ENV")));
// remainder of test
}
@Test
void testOnlyOnDeveloperWorkstation() {
assumeTrue("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, 2);
});
// perform these assertions in all environments
assertEquals("a string", "a string");
}
禁用测试
@Disabled 可以注解到类上,使整个类禁用测试
条件测试
当在某个条件是才测试
@Test
@EnabledOnOs(MAC)
void onlyOnMacOs() {
// ...
}
@TestOnMac
void testOnMac() {
// ...
}
@Test
@EnabledOnOs({ LINUX, MAC })
void onLinuxOrMac() {
// ...
}
@Test
@DisabledOnOs(WINDOWS)
void notOnWindows() {
// ...
}
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
@Test
@EnabledOnOs(MAC)
@interface TestOnMac {
}
@Test
@EnabledOnJre(JAVA_8)
void onlyOnJava8() {
// ...
}
@Test
@EnabledOnJre({ JAVA_9, JAVA_10 })
void onJava9Or10() {
// ...
}
@Test
@DisabledOnJre(JAVA_9)
void notOnJava9() {
// ...
}
基于脚本的执行
@Test // Static JavaScript expression.
@EnabledIf("2 * 3 == 6")
void willBeExecuted() {
// ...
}
@RepeatedTest(10) // Dynamic JavaScript expression.
@DisabledIf("Math.random() < 0.314159")
void mightNotBeExecuted() {
// ...
}
@Test // Regular expression testing bound system property.
@DisabledIf("/32/.test(systemProperty.get('os.arch'))")
void disabledOn32BitArchitectures() {
assertFalse(System.getProperty("os.arch").contains("32"));
}
@Test
@EnabledIf("'CI' == systemEnvironment.get('ENV')")
void onlyOnCiServer() {
assertTrue("CI".equals(System.getenv("ENV")));
}
@Test // Multi-line script, custom engine name and custom reason.
@EnabledIf(value = {
"load('nashorn:mozilla_compat.js')",
"importPackage(java.time)",
"",
"var today = LocalDate.now()",
"var tomorrow = today.plusDays(1)",
"tomorrow.isAfter(today)"
},
engine = "nashorn",
reason = "Self-fulfilling: {result}")
void theDayAfterTomorrow() {
LocalDate today = LocalDate.now();
LocalDate tomorrow = today.plusDays(1);
assertTrue(tomorrow.isAfter(today));
}
函数注入
@DisplayName("TestInfo Demo")
public class DisplayNameDemo {
DisplayNameDemo(TestInfo testInfo) {
assertEquals("TestInfo Demo", testInfo.getDisplayName());
}
@BeforeEach
void init(TestInfo testInfo) {
String displayName = testInfo.getDisplayName();
assertTrue(displayName.equals("TEST 1") || displayName.equals("test2()"));
}
@Test
@DisplayName("TEST 1")
@Tag("my-tag")
void test1(TestInfo testInfo) {
assertEquals("TEST 1", testInfo.getDisplayName());
assertTrue(testInfo.getTags().contains("my-tag"));
}
@Test
void test2() {
}
}
参数注解
下面的@ParameterizedTest方法将被调用三次,用这些值1,2和3分别。
@ParameterizedTest
@ValueSource(ints = { 1, 2, 3 })
void testWithValueSource(int argument) {
assertTrue(argument > 0 && argument < 4);
}
@EnumSource
@EnumSource提供了一种使用Enum常量的简便方法。该注释提供了一个可选names参数,可让您指定应使用哪些常量。如果省略,所有常量将如下例所示使用。
@ParameterizedTest
@EnumSource(TimeUnit.class)
void testWithEnumSource(TimeUnit timeUnit) {
assertNotNull(timeUnit);
}
@ParameterizedTest
@EnumSource(value = TimeUnit.class, names = { "DAYS", "HOURS" })
void testWithEnumSourceInclude(TimeUnit timeUnit) {
assertTrue(EnumSet.of(TimeUnit.DAYS, TimeUnit.HOURS).contains(timeUnit));
}
该@EnumSource注释还提供了可选的mode,使在其上常数传递给测试方法精细控制参数。例如,您可以从枚举常量池中排除名称或指定正则表达式,如以下示例中所示。
@ParameterizedTest
@EnumSource(value = TimeUnit.class, mode = EXCLUDE, names = { "DAYS", "HOURS" })
void testWithEnumSourceExclude(TimeUnit timeUnit) {
assertFalse(EnumSet.of(TimeUnit.DAYS, TimeUnit.HOURS).contains(timeUnit));
assertTrue(timeUnit.name().length() > 5);
}
@ParameterizedTest
@EnumSource(value = TimeUnit.class, mode = MATCH_ALL, names = "^(M|N).+SECONDS$")
void testWithEnumSourceRegex(TimeUnit timeUnit) {
String name = timeUnit.name();
assertTrue(name.startsWith("M") || name.startsWith("N"));
assertTrue(name.endsWith("SECONDS"));
}
@MethodSource
@MethodSource允许您引用测试类或外部类的一个或多个工厂方法。这样的工厂方法必须返回一个Stream,Iterable,Iterator,或参数数组。另外,这种工厂方法不能接受任何论点。测试类中的工厂方法必须是static除非测试类注有@TestInstance(Lifecycle.PER_CLASS); 而外部类中的工厂方法必须始终如一static。
如果您只需要一个参数,则可以返回一个Stream参数类型的实例,如以下示例所示。
@ParameterizedTest
@MethodSource("stringProvider")
void testWithSimpleMethodSource(String argument) {
assertNotNull(argument);
}
static Stream<String> stringProvider() {
return Stream.of("foo", "bar");
}
如果您没有明确提供工厂方法名称@MethodSource,则JUnit Jupiter将按照惯例搜索与当前方法名称相同的工厂方法 @ParameterizedTest。以下示例演示了这一点。
@ParameterizedTest
@MethodSource
void testWithSimpleMethodSourceHavingNoValue(String argument) {
assertNotNull(argument);
}
static Stream<String> testWithSimpleMethodSourceHavingNoValue() {
return Stream.of("foo", "bar");
}
以下示例说明了原始类型(DoubleStream,,IntStream和LongStream)的流也受支持。
@ParameterizedTest
@MethodSource("range")
void testWithRangeMethodSource(int argument) {
assertNotEquals(9, argument);
}
static IntStream range() {
return IntStream.range(0, 20).skip(10);
}
如果测试方法声明多个参数,则需要返回Arguments如下所示的集合或实例流。请注意,这Arguments.of(Object…)是在Arguments界面中定义的静态工厂方法。
@ParameterizedTest
@MethodSource("stringIntAndListProvider")
void testWithMultiArgMethodSource(String str, int num, List<String> list) {
assertEquals(3, str.length());
assertTrue(num >=1 && num <=2);
assertEquals(2, list.size());
}
static Stream<Arguments> stringIntAndListProvider() {
return Stream.of(
Arguments.of("foo", 1, Arrays.asList("a", "b")),
Arguments.of("bar", 2, Arrays.asList("x", "y"))
);
}