一,JUnit5 动态测试
1,概念
- 动态测试是 JUnit5 中引入的一种新的编程模型。
- 这种新的测试就是动态测试,它是由@TestFactory注解的工厂方法在运行时生成的。
2,动态测试与静态测试的区别
- 动态测试:就是DynamicTest在==运行的时候动态的生成测试用例,由@TestFactory注解声明的方法。
- 静态测试:@Test注解的测试用例,因为该用例在编译时已经完全指定好的。
- 与@Test方法相比,@TestFactory方法本身不是测试用例,而是测试用例的工厂。
- 所以得到结论:动态测试是工厂的产物。
- 动态测试DynamicTest与标准 @Test 用例完全不同
- 执行方式不同:动态测试「DynamicTest」不支持生命周期回调。
3,动态测试构成
(1)方法上必须有 @TestFactory 注解。
(2)由显示名称和Executable组成。
(3)必须有方法返回值类型,类型为「流、集合、迭代器」。
Stream Collection Iterable Iterator DynamicNode
- 不返回会报JUnitException异常
import org.junit.jupiter.api.*;
import java.util.Arrays;
import java.util.Collection;
import static org.junit.jupiter.api.Assertions.assertEquals;
public class DynamicDemoTest {
@Test
@DisplayName("静态加法测试用例")
void add(){
assertEquals(5,2+3);
}
@Test
@DisplayName("静态除法测试用例")
void devide(){
assertEquals(5,25/5);
}
//将静态用例转化为动态测试用例
@TestFactory
@DisplayName("动态测试用例")
Collection<DynamicNode> dynamicForCollection(){
DynamicTest dynamicTest = DynamicTest.dynamicTest("动态加法测试用例", () -> {
System.out.println("动态加法测试用例运行");
assertEquals(6, 2 + 14);
});
DynamicTest dynamicTest1 = DynamicTest.dynamicTest("动态除法测试用例", () -> {
assertEquals(6, 36/6);
});
return Arrays.asList(dynamicTest,dynamicTest1);
}
}
4,动态测试创建规则
(1)方法不能是 private
,static
修饰。
(2)返回类型必须为: Stream、Collection、Iterable、或Iterator
、DynamicNode
。
import com.fasterxml.jackson.databind.ser.std.StdKeySerializers;
import org.junit.jupiter.api.DisplayName;
import org.junit.jupiter.api.DynamicContainer;
import org.junit.jupiter.api.DynamicTest;
import org.junit.jupiter.api.TestFactory;
import static org.hamcrest.Matchers.*;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Iterator;
import java.util.PrimitiveIterator;
import java.util.stream.Collectors;
import java.util.stream.IntStream;
import java.util.stream.Stream;
import static org.hamcrest.MatcherAssert.assertThat;
import static org.hamcrest.Matchers.greaterThan;
import static org.hamcrest.core.Is.is;
import static org.junit.jupiter.api.Assertions.assertEquals;
public class DynamicNodeTest {
//DynamicNode返回类型,有两个实例类:DynamicTest和DynamicContainer
@TestFactory
@DisplayName("单个动态测试")
DynamicTest dynamicTest(){
return DynamicTest.dynamicTest("DynamicTest",() ->{
assertEquals(7,3+4);
});
}
@TestFactory
@DisplayName("多个动态测试容器")
DynamicContainer dynamicContainer(){
return DynamicContainer.dynamicContainer("DynamicContainer",
Stream.of(
DynamicTest.dynamicTest("加法动态测试",() ->{
System.out.println("这是一个加法的测试用例!!");
assertEquals(8,4+4);
}),
DynamicTest.dynamicTest("除法动态测试",() ->{
System.out.println("这是一个除法的测试用例!!");
assertEquals(8,16/2);
})
)
);
}
@TestFactory
@DisplayName("Stream单个动态测试")
Stream<DynamicTest> streamTest(){
return Stream.of(5,6,7).map(
arg ->DynamicTest.dynamicTest("Stream动态测试",() ->{
System.out.println("参数:"+arg);
assertThat(arg,is(greaterThan(2)));
}
)
);
}
@TestFactory
@DisplayName("Stream多个动态测试")
Stream<DynamicContainer> streamTest2(){
return Stream.of(5,6,7).map(
arg -> DynamicContainer.dynamicContainer("container多个动态测试",
Stream.of(
DynamicTest.dynamicTest("Stream大于动态测试", () -> {
System.out.println("参数:" + arg);
assertThat(arg, is(greaterThan(2)));
}),
DynamicTest.dynamicTest("Stream大于动态测试", () -> {
System.out.println("参数:" + arg);
assertThat(arg, is(greaterThan(4)));
}
)
)
)
);
}
@TestFactory
@DisplayName("Collection动态测试")
Collection<DynamicTest> testCollection(){
Collection<DynamicTest> list = new ArrayList<>();
DynamicTest dynamicTest = DynamicTest.dynamicTest("Collection加法测试", () -> {
assertEquals(7, 3 + 4);
});
DynamicTest dynamicTest1 = DynamicTest.dynamicTest("Collection除法测试", () -> {
assertEquals(15, 75/5);
});
list.add(dynamicTest);
list.add(dynamicTest1);
return list;
}
//返回类型为迭代器
@TestFactory
@DisplayName("Iterator动态测试")
Iterator<DynamicTest> testIterator(){
Collection<DynamicTest> iteratorList = new ArrayList<>();
PrimitiveIterator.OfInt ofInt = IntStream.iterate(3, n -> n + 1).limit(3).iterator();
while(ofInt.hasNext()){
Integer next = ofInt.next();
DynamicTest dynamicTest = DynamicTest.dynamicTest("n+1动态测试", () -> {
System.out.println("参数n= " + next);
assertThat(next, is(greaterThan(2)));
});
DynamicTest dynamicTest1 = DynamicTest.dynamicTest("n+2动态测试", () -> {
int a = next;
a +=1;
System.out.println("参数n= " + a);
assertThat(a, is(greaterThan(3)));
});
iteratorList.add(dynamicTest);
iteratorList.add(dynamicTest1);
}
return iteratorList.iterator();
}
//返回类型为迭代器元素
@TestFactory
@DisplayName("Iterable动态测试")
Iterable<DynamicTest> testIterable(){
Collection<DynamicTest> dynamicTests = new ArrayList<>();
PrimitiveIterator.OfInt ofInt = IntStream.iterate(3, n -> n + 1).limit(3).iterator();
while(ofInt.hasNext()){
Integer next = ofInt.next();
DynamicTest dynamicTest = DynamicTest.dynamicTest("n+1动态测试", () -> {
System.out.println("参数n= " + next);
assertThat(next, is(greaterThan(2)));
});
DynamicTest dynamicTest1 = DynamicTest.dynamicTest("n+2动态测试", () -> {
int a = next;
a +=1;
System.out.println("参数n= " + a);
assertThat(a, is(greaterThan(3)));
});
dynamicTests.add(dynamicTest);
dynamicTests.add(dynamicTest1);
}
return dynamicTests;
}
}
5,动态测试生命周期
- 在动态测试中,
@BeforeEach
和@AfterEach
生命周期方法会针对每个@TestFactory
方法执行 - 不会针对每个动态测试执行。
- 静态测试用例的每一个参数用例都会执行一次
BeforeEach
和AfterEach
-
Dynamic Test
的方法只会执行一次BeforeEach
和AfterEach
- 默认把
BeforeEach
和AfterEach
的生命周期提升为了BeforeAll
、AfterAll
- 默认把
6,动态测试执行顺序
- 动态测试的方法顺序不依赖于在顶级测试类上声明的@TestMethodOrder。
- 为了控制动态测试的顺序,我们可以使用自定义排序。
import org.junit.jupiter.api.DisplayName;
import org.junit.jupiter.api.DynamicTest;
import org.junit.jupiter.api.TestFactory;
import java.net.NetworkInterface;
import java.util.Arrays;
import java.util.Collection;
import java.util.List;
public class DynamicOrderTest {
@TestFactory
@DisplayName("多条动态测试用例")
List<DynamicTest> dynamicTestCollect(){
DynamicTest dynamicTest1 = DynamicTest.dynamicTest("1stDy", () -> {
System.out.println("1stDy");
});
DynamicTest dynamicTest2 = DynamicTest.dynamicTest("2ndtDy", () -> {
System.out.println("2ndDy");
});
DynamicTest dynamicTest3 = DynamicTest.dynamicTest("3rdtDy", () -> {
System.out.println("3rdDy");
});
List<DynamicTest> lists = Arrays.asList(dynamicTest1,dynamicTest2,dynamicTest3);
sortDy(lists);
return lists;
}
//自定义动态测试执行顺序方法
static void sortDy(List<DynamicTest> lists){
lists.sort((DynamicTest t1, DynamicTest t2) -> t2.getDisplayName().compareTo(t1.getDisplayName())
);
}
}
7,Junit5测试生命周期回调
回调接口:
- BeforeAllCallback (在所有测试方法 及
@BeforeAll
注解修饰的方法执行之前执行) - AfterAllCallback ( 在所有测试方法 及
@AfterAll
注解修饰的方法 执行之后执行) - BeforeEachCallBack ( 在每个测试方法及
@BeforeEach
注解修饰的方法之前执行) - AfterEachCallback ( 在每个测试方法及
@AfterEach
注解修饰的方法之后执行) - BeforeTestExecutionCallback ( 在测试方法@Test 之前立即执行)
- AfterTestExecutionCallback ( 在测试方法@Test 之后立即执行)
执行顺序:
BeforeAllCallback
BeforeAll
BeforeEachCallback
BeforeEach
BeforeTestExecutionCallback
Test
AfterTestExecutionCallback
AfterEach
AfterEachCallback
AfterAll
AfterAllCallback
通过@ExtendWith 注解进行回调激活
- 新建一个实体类实现回调接口
import org.junit.jupiter.api.extension.*;
public class CallbackExtension implements BeforeAllCallback, AfterAllCallback, BeforeEachCallback, AfterEachCallback, BeforeTestExecutionCallback,AfterTestExecutionCallback {
@Override
public void beforeAll(ExtensionContext extensionContext) throws Exception {
System.out.println("CallbackExtension —— BeforeAllCallback");
}
@Override
public void afterAll(ExtensionContext extensionContext) throws Exception {
System.out.println("CallbackExtension —— AfterAllCallback");
}
@Override
public void beforeEach(ExtensionContext extensionContext) throws Exception {
System.out.println("CallbackExtension —— BeforeEachCallback");
}
@Override
public void afterEach(ExtensionContext extensionContext) throws Exception {
System.out.println("CallbackExtension —— AfterEachCallback");
}
@Override
public void beforeTestExecution(ExtensionContext extensionContext) throws Exception {
System.out.println("CallbackExtension —— BeforeTestExecutionCallback");
}
@Override
public void afterTestExecution(ExtensionContext extensionContext) throws Exception {
System.out.println("CallbackExtension —— AfterTestExecutionCallback");
}
}
- 在测试类上加上@ExtendWith 注解进行回调激活,查看测试用例执行顺序
import org.junit.jupiter.api.*;
import org.junit.jupiter.api.extension.ExtendWith;
@ExtendWith({CallbackExtension.class})
public class OneTest {
@BeforeAll
static void beforeAll(){
System.out.println("OneTest - BeforeAll"); }
@BeforeEach
void beforeEach(){
System.out.println("OneTest - BeforeEach"); }
@Test
void test01(){
System.out.println("OneTest - test01测试用例"); }
@Test
void test02(){
System.out.println("OneTest - test02测试用例"); }
@AfterEach
void afterEach(){
System.out.println("OneTest - AfterEach"); }
@AfterAll
static void afterAll(){
System.out.println("OneTest - AfterAll"); }
}