引入 mvn 依赖
<!-- JUnit5 -->
<dependency>
<groupId>org.junit.jupiter</groupId>
<artifactId>junit-jupiter-engine</artifactId>
<version>5.5.2</version>
<scope>test</scope>
</dependency>
<!-- JUnit4 的兼容包 -->
<dependency>
<groupId>org.junit.vintage</groupId>
<artifactId>junit-vintage-engine</artifactId>
<version>5.5.2</version>
<scope>test</scope>
</dependency>
<!-- JUnit5 参数化测试 API -->
<dependency>
<groupId>org.junit.jupiter</groupId>
<artifactId>junit-jupiter-params</artifactId>
<version>5.5.2</version>
<scope>test</scope>
</dependency>
JUnit5常用注解
import org.junit.jupiter.api.*;
@Tag("标签1")
@DisplayName("使用基本注解")
public class BaseAnnotation {
@BeforeAll
public static void beforeAll() {
System.out.println("仅在第一个测试方法之前执行,且只执行一次");
}
@AfterAll
public static void afterAll() {
System.out.println("仅在第一个测试方法之后执行,且只执行一次");
}
@BeforeEach
public void beforeEach() {
System.out.println("每个测试方法前都会执行");
}
@AfterEach
public void afterEach() {
System.out.println("每个测试方法后都会执行");
}
@Tag("标签1")
@DisplayName("测试方法1")
@Test
void displayName() {
System.out.println("测试方法1");
}
@Tag("标签1")
@DisplayName("测试方法2")
@Test
void testForthTest() {
System.out.println("测试方法2");
}
@Tag("标签2")
@DisplayName("标签2-方法1")
@Test
void testSecondTest() {
System.out.println("我的第二个测试开始测试");
}
@Tag("标签2")
@Disabled
@DisplayName("标签2-方法2-@disabled")
@Test
void disabled() {
System.out.println("@Disabled 禁用测试方法");
}
@DisplayName("重复测试方法")
@RepeatedTest(2)
void repeatTest(){
System.out.println("重复测试执行");
}
@Nested
@DisplayName("嵌套-测试类")
class FirstNestTest {
@DisplayName("嵌套-测试方法A1")
@Test
void test() {
System.out.println("测试方法A1");
}
}
}
JUnit5断言与假设
import org.junit.jupiter.api.Assumptions;
import org.junit.jupiter.api.DisplayName;
import org.junit.jupiter.api.Test;
import java.time.Duration;
import static org.junit.jupiter.api.Assertions.*;
/**
* {@link org.junit.jupiter.api.Assertions}
* {@link org.junit.jupiter.api.Assumptions}
*/
@DisplayName("使用断言与假设工具")
public class AssertUtil {
/**
* 断言:当某一断言不符合预期结果时会抛出 AssertionError extends Error 终止当前线程。
*/
@Test
void assertions(){
/* java 中的 boolean 值断言关键字 assert */
boolean assertable = Math.random() != Math.random();
assert assertable : "impossible is nothing!!!";
/* JUnit5 提供的断言方法 org.junit.jupiter.api.Assertions.* */
/* 断言信息 */
assertTrue(true,"不为 true 时输出的信息");
/* 值断言 */
assertTrue(true);
assertFalse(false);
assertNull(null);
assertNotNull(new Object());
assertEquals(5,5);
assertNotEquals(new int[]{1,2,3,4,5},new int[]{1,2,3,4,5});
assertArrayEquals(new int[]{1,2,3,4,5},new int[]{1,2,3,4,5});
assertSame(1,1);
assertNotSame(new int[]{1,2,3,4,5},new int[]{1,2,3,4,5});
/* 分组断言 */
assertAll(
()->{ System.out.println("批量断言-0"); },
()->{ System.out.println("批量断言-1"); },
()->{ System.out.println("批量断言-2"); }
);
/* 异常或失败断言 */
assertThrows(Exception.class,()->{throw new Exception("抛出异常");});
assertDoesNotThrow(()->{System.out.println("不抛出异常");});
/* 同步的超时断言 */
assertTimeout(Duration.ofSeconds(4),()->{
Thread.sleep(3000);
});
/* 抢断的超时断言 */
assertTimeoutPreemptively(Duration.ofSeconds(4),()->{
Thread.sleep(3000);
});
}
/**
* 假设:通过抛出 IncompleteExecutionException extends RuntimeException 异常结束执行,该异常由 JUnit 处理。
* 所以 Assumptions 不会导致测试失败。
*/
@Test
void assumptions() {
//System.getenv().forEach((key, value) -> System.out.println(key + " -> " + value));
Assumptions.assumeTrue(!"Windows_NT".equals(System.getenv("OS")));
Assumptions.assumeTrue("Windows_NT".equals(System.getenv("OS")));
System.out.println("Assumptions");
}
}
JUnit5 参数化测试
import org.junit.jupiter.api.DisplayName;
import org.junit.jupiter.api.extension.ExtensionContext;
import org.junit.jupiter.params.ParameterizedTest;
import org.junit.jupiter.params.provider.*;
import java.util.EnumSet;
import java.util.concurrent.TimeUnit;
import java.util.stream.Stream;
import static org.junit.jupiter.api.Assertions.assertFalse;
import static org.junit.jupiter.api.Assertions.assertNotNull;
import static org.junit.jupiter.params.provider.EnumSource.Mode.EXCLUDE;
import static org.junit.jupiter.params.provider.EnumSource.Mode.MATCH_ALL;
@DisplayName("参数化测试")
public class ParamsTest {
@DisplayName("@ValueSource 值参数")
@ValueSource(strings = {"Effective Java","Code Complete","Clean Code"})
@ParameterizedTest
void valueSourceTest(String str){
System.out.println(str);
}
@DisplayName("@CsvSource CSV格式字符串")
@CsvSource(value = {"1@one","2@two","3@three"},delimiter = '@')
@ParameterizedTest
void csvSourceTest(long id,String name){
System.out.printf("id: %d, name: %s\n",id,name);
}
@DisplayName("@CsvFileSource CSV文件")
@CsvFileSource(resources = "/test.csv", delimiter = ',' )
@ParameterizedTest
void testDataFromCsv(long id, String name) {
System.out.printf("id: %d, name: %s\n", id, name);
/*
test.csv
1,one
2,two
3,three
*/
}
@DisplayName("@EnumSource 枚举类")
@EnumSource(value = TimeUnit.class, mode = EXCLUDE, names = { "DAYS", "HOURS" })
@ParameterizedTest
void testWithEnumSourceExclude(TimeUnit timeUnit) {
EnumSet<TimeUnit> timeUnitEnumSet = EnumSet.of(TimeUnit.DAYS, TimeUnit.HOURS);
assertFalse(timeUnitEnumSet.contains(timeUnit));
}
@EnumSource(value = TimeUnit.class, mode = MATCH_ALL, names = "^(M|N).+SECONDS$")
@ParameterizedTest
void testWithEnumSourceExcludeRegEx(TimeUnit timeUnit) {
assertFalse(EnumSet.of(TimeUnit.DAYS, TimeUnit.HOURS).contains(timeUnit));
}
@MethodSource("stringProvider")
@ParameterizedTest
void testWithSimpleMethodSource(String argument) {
assertNotNull(argument);
}
static Stream<String> stringProvider() {
return Stream.of("foo", "bar");
}
@ArgumentsSource(ArgumentsProviderImpl.class)
@ParameterizedTest
void testWithArgumentSource(String arg){
assertNotNull(arg);
}
static class ArgumentsProviderImpl implements ArgumentsProvider{
@Override
public Stream<? extends Arguments> provideArguments(ExtensionContext context) throws Exception {
return Stream.of("foo", "bar").map(Arguments::of);
}
}
}