🤗 ApiHug × {Postman|Swagger|Api...} = 快↑ 准√ 省↓
- GitHub - apihug/apihug.com: All abou the Apihug
- apihug.com: 有爱,有温度,有质量,有信任
- ApiHug - API design Copilot - IntelliJ IDEs Plugin | Marketplace
涉及到 Junit5 一些深度不太常用的功能, 例子在:advancedopen in new window。
#顺序
JUnit 5 支持下面几种 MethodOrderer:
- MethodName – 按照函数名字的字母排序。
- DisplayName – 按照显示名词的字母排序。
- OrderAnnotation – 按照 @Order annotation 排序,同order 排序方式可能不一致(所以尽量order 是散列的, 未标注的排最后面。
- Random – 随机排序 种子值可以通过系统配置: junit.jupiter.execution.order.random.seed 来配置。
- Custom – 定制扩展的排序类。
MethodNameOrderedTestsopen in new window 例子:
#MethodName
@TestMethodOrder(MethodOrderer.MethodName.class)
public class MethodNameOrderedTests
{
@Test
void testE() {
assertTrue(true);
}
}
#DisplayName
@TestMethodOrder(MethodOrderer.DisplayName.class)
public class DisplayNameOrderedTests
{
@DisplayName("5")
@Test
void testE() {
assertTrue(true);
}
}
#OrderAnnotation
@TestMethodOrder(MethodOrderer.OrderAnnotation.class)
public class OrderAnnotationTests {
@Order(5)
@Test
void testE() {
assertTrue(true);
}
}
#Random
修改 junit-platform.properties
junit.jupiter.execution.order.random.seed=9999
这个测试类, 每次执行的结果顺序都讲不一样!
@TestMethodOrder(MethodOrderer.Random.class)
public class RandomOrderedTests {
@Test
void testE() {
assertTrue(true);
}
}
#Custom
比如一个实现按照接口是否被 Deprecated
, 排到最后进行测试:
public class DeprecatedInEndTestOrder implements MethodOrderer {
private Comparator<MethodDescriptor> comparator =
Comparator.comparing(md -> md.getMethod().isAnnotationPresent(Deprecated.class));
@Override
public void orderMethods(MethodOrdererContext context) {
context.getMethodDescriptors().sort(comparator);
}
}
然后参考测试用例:
@TestMethodOrder(DeprecatedInEndTestOrder.class)
public class CustomOrderTests {
@Test
@Deprecated
void testC() {
assertTrue(true);
}
@Test
void testA() {
assertTrue(true);
}
}
当然也有正对,测试类自己的排序,这里不再赘述, 参考样例代码。
默认配置: junit.jupiter.testclass.order.default = org.junit.jupiter.api.ClassOrderer$OrderAnnotation
@TestClassOrder(ClassOrderer.OrderAnnotation.class)
class OrderedTestClassesExample {
@Nested
@Order(1)
class SetupTests {
@Test
void test1() {}
}
@Nested
@Order(2)
class AppFlowTests {
@Test
void test2() {}
}
}
#Temp Dir
@TempDir 尚在实验功能open in new window 但是在一些场景下非常有用。
@TempDir
可以使用在方法参数, 类参数, 静态方法,可以用于构建 Fixture, 举个例子 测试目录open in new window:
@Test
void tempDirectoryTestOne(@TempDir Path tempDir) throws IOException {
Path tempFile = tempDir.resolve("test.txt");
List<String> lines = Arrays.asList("dearxue.com");
Files.write(tempFile, Arrays.asList("dearxue.com"));
Assertions.assertTrue(Files.exists(tempFile), "Temp File should have been created");
Assertions.assertEquals(Files.readAllLines(tempFile), lines);
}
@TempDir
作为成员变量, 那么每个测试用例都会创建一个新的临时测试目录, 然后再删除参考 TempDirTest2。
test1 dir: C:\Users\dearxue\AppData\Local\Temp\junit3449074755074304313
test2 dir: C:\Users\dearxue\AppData\Local\Temp\junit7411092462858685015
@TempDir
作为静态成员变量, 和成员变量不一样, 这里一个测试类中不同测试方法会共享一个目录。
#测试名称
在一些场景下你希望获取测试名词, 可以通过 TestInfo
上下文获得 gettestname 例子open in new window:
@Test
void givenNumbers_whenOddCheck_thenVerify(TestInfo testInfo) {
System.out.println("displayName = " + testInfo.getDisplayName());
int number = 5;
assertTrue(oddCheck(number));
}
获得输出:
displayName = givenNumbers_whenOddCheck_thenVerify(TestInfo)
参数化场景可以通过 @BeforeEach
获得:
private TestInfo testInfo;
@BeforeEach
void init(TestInfo testInfo) {
this.testInfo = testInfo;
}
private boolean oddCheck(int number) {
return (number % 2 != 0);
}
@ParameterizedTest(name = "givenNumbers_whenOddCheck_thenVerify{0}")
@ValueSource(ints = {1, 3, 5, -3, 15})
void givenNumbers_whenOddCheck_thenVerify(int number) {
System.out.println("displayName = " + testInfo.getDisplayName());
assertTrue(oddCheck(number));
}
输出为:
displayName = givenNumbers_whenOddCheck_thenVerify5
displayName = givenNumbers_whenOddCheck_thenVerify-3
displayName = givenNumbers_whenOddCheck_thenVerify3
displayName = givenNumbers_whenOddCheck_thenVerify1
displayName = givenNumbers_whenOddCheck_thenVerify15
#Test Instance
默认情况下, Junit 会为每个测试方法创建一个新的instance,这样保持状态的隔离:
class AdditionTest {
private int sum = 1;
@Test
void addingTwoReturnsThree() {
sum += 2;
assertEquals(3, sum);
}
@Test
void addingThreeReturnsFour() {
sum += 3;
assertEquals(4, sum);
}
}
比如这个测试用例, 两个测试方法执行前, 新的事例被创建, sum 被初始化为1, 所以每个方法都能成立。
如果共享一个事例, 那么第二个方法会失败。
但是在一些情况下我们需要, 共享这些变量, 比如一些非常耗时的操作, 这个时候 TestInstance
派上用场了。
@TestInstance
两种模式一个是:LifeCycle.PER_METHOD (默认的), 另外一个是LifeCycle.PER_CLASS。
在 PER_CLASS
模式下, 我们可以看到下面例子的变量和方法都不是 static
但是能够照常运行。
@TestInstance(LifeCycle.PER_CLASS)
class TweetSerializerUnitTest {
private String largeContent;
@BeforeAll
void setUpFixture() {
// read the file
}
#手动启动测试
你可以直接从 java
启动测试用例, 对整个junit 运行逐层debug,洞悉运行过程。
testRuntimeOnly("org.junit.platform:junit-platform-launcher") {
because 'allows tests to run from IDEs that bundle older version of launcher'
}
添加依赖, 例子参考open in new window
public void runOne() {
LauncherDiscoveryRequest request =
LauncherDiscoveryRequestBuilder.request()
.selectors(selectClass(FirstUnitTest.class))
.build();
Launcher launcher = LauncherFactory.create();
TestPlan testPlan = launcher.discover(request);
launcher.registerTestExecutionListeners(listener);
launcher.execute(request);
}
输出:
junit-5:RunJUnit5TestsFromJava.main()
Test run finished after 171 ms
[ 6 containers found ]
[ 0 containers skipped ]
[ 6 containers started ]
[ 0 containers aborted ]
[ 6 containers successful ]
[ 0 containers failed ]
[ 18 tests found ]
[ 0 tests skipped ]
[ 18 tests started ]
[ 0 tests aborted ]
[ 18 tests successful ]
[ 0 tests failed ]