🤗 ApiHug × {Postman|Swagger|Api...} = 快↑ 准√ 省↓
- GitHub - apihug/apihug.com: All abou the Apihug
- apihug.com: 有爱,有温度,有质量,有信任
- ApiHug - API design Copilot - IntelliJ IDEs Plugin | Marketplace
这里涉及到 Junit5 更深内容 Extension Modelopen in new window;
在 Junit4 中有, Runner, TestRule, & MethodRule 扩展点, 在Junit 5中归一为 API Extension , 不过这也仅仅是个 marker interface。
Spring Test Junit4 Extenstionopen in new window vs Spring Test Jupiter Extenstionopen in new window, 两个正好体现 Junit 4&5 之间的差别。
Junit4 的扩展局限性:
- 必须在测试类级别上使用
@RunWith
注解来声明Runner
。 @RunWith
仅接受一个参数:Runner
的实现类。因为每个测试类最多只能拥有一个Runner
,所以每个测试类最多也只能拥有一个扩展点。(在PowerMock中引入了@PowerMockRunnerDelegate
,可以同时使用两个Runner)- 为了解决 Runner 的限制,JUnit 4.7 引入了
@Rule
。一个测试类可声明多个@Rule
,这些规则可在类级别和测试方法级别上运行,但是它只能在测试运行之前或之后执行指定操作。如果我们想在此之外的时间点进行扩展,@Rule
也无法满足我们的要求。
JUnit 5扩展机制的核心准则: Prefer extension points over features
; 基于这一准则,JUnit 5 中定义了许多扩展点,每个扩展点都对应一个接口。我们可以定义自己的扩展可以实现其中的某些接口,然后通过 @ExtendWith
注解注册给 JUnit,后者会在特定的时间点调用注册的接口实现。
JUnit5 定义的部分扩展点:
接口 | 说明 |
---|---|
BeforeAllCallback | 在所有测试方法执行前定义测试容器执行的行为,也就是在 @BeforeAll 注解的方法之前执行 |
AfterAllCallback | 在@AfterAll 注解的方法之后执行 |
BeforeEachCallback | 在@BeforeEach 注解的方法之前执行 |
AfterEachCallback | 在@AfterEach 注解的方法之后执行 |
BeforeTestExecutionCallback | 在测试方法运行之前立刻执行 |
AfterTestExecutionCallback | 在测试方法运行之后立刻执行 |
ParameterResolver | 用于在运行时动态解析参数 |
Extension Context
Extension Context包含了测试方法的上下文信息,所有的扩展接口都定义了一个包含该参数的接口方法,开发者可以根据Extension Context拿到跟测试方法相关的几乎所有的信息,包括方法注解,测试实例,测试标签等。
Store
Extension Context 提供了一个getStore(Namespace namespace)的接口方法,返回了一个Store的对象,用于存储一些数据,方便不同的回调接口之间共享数据。
Namespace
如果你需要使用Extension Context里的Store功能,那么你就需要先申请一个Namespace,它可以避免不同的扩展实现误操作同一份数据
#统计执行时间
extensions>benchmarkopen in new window 例子:
class BenchmarkExtensionimplements BeforeAllCallback,BeforeTestExecutionCallback,AfterTestExecutionCallback,AfterAllCallback {}
BenchmarkExtension
定义扩展点:
- 计算所有测试类的运行时间,在所有测试执行前保存其起始时间
- 计算每个测试方法的运行时间,在每个测试方法执行前保存其起始时间
- 在每个测试方法执行完毕后,获取其结束时间,计算并输出该测试方法的运行时间
- 在所有测试类执行完毕后,获取其结束时间,计算并输出所有测试的运行时间
- 对所有注解了
@BenchMark
的测试类或测试方法生效
定义 @Benchmark
@Target({ TYPE, METHOD, ANNOTATION_TYPE })
@Retention(RetentionPolicy.RUNTIME)
@ExtendWith(BenchmarkExtension.class)
public @interface Benchmark { }
测试类:
@Benchmark
public class BenchmarkTest {@Test@Benchmarkpublic void test_1() {try {Thread.sleep(2_000);} catch (InterruptedException e) {e.printStackTrace();}}@Testpublic void test_2() {try {Thread.sleep(1_000);} catch (InterruptedException e) {e.printStackTrace();}}
}
获得测试输出:
Test 'test_1()' took 2033 ms.
Test container 'BenchmarkTest' took 2115 ms.
#Test Watcher
TestWatcheropen in new window 接口定义了API扩展,提供钩子函数对测试处理结果进行进一步的处理。
比如不同过的测试状态: testDisabled
testSuccessful
等等。
public class TestResultLoggerExtension implements TestWatcher, AfterAllCallback {}
测试用例:
@ExtendWith(TestResultLoggerExtension.class)
class TestWatcherAPIUnitTest {
}
测试输出
Test Disabled for test givenFailure_whenTestDisabledWithReason_ThenCaptureResult(): with reason :- This test is disabled
....
Test result summary for TestWatcherAPIUnitTest {ABORTED=1, SUCCESSFUL=1, DISABLED=2}
#Parameter Resolver
我们对 @Random
标注参数, 从上下文注入随机参数 junit5-jupiter-extensions>randomopen in new window:
public class RandomParametersExtension implements ParameterResolver {@Retention(RetentionPolicy.RUNTIME)@Target(ElementType.PARAMETER)public @interface Random {}@Overridepublic boolean supportsParameter(ParameterContext parameterContext, ExtensionContext extensionContext) {return parameterContext.isAnnotated(Random.class);}@Overridepublic Object resolveParameter(ParameterContext parameterContext, ExtensionContext extensionContext) {return getRandomValue(parameterContext.getParameter(), extensionContext);}private Object getRandomValue(Parameter parameter, ExtensionContext extensionContext) {Class<?> type = parameter.getType();java.util.Random random =extensionContext.getRoot().getStore(Namespace.GLOBAL) //.getOrComputeIfAbsent(java.util.Random.class);if (int.class.equals(type)) {return random.nextInt();}if (double.class.equals(type)) {return random.nextDouble();}throw new ParameterResolutionException("No random generator implemented for " + type);}
}
测试用例:
@ExtendWith(RandomParametersExtension.class)
class RandomParametersExtensionTests {@Testvoid injectsInteger(@Random int i, @Random int j) {assertNotEquals(i, j);}@Testvoid injectsDouble(@Random double d) {assertEquals(0.0, d, 1.0);}
}
#参考
- 更复杂关于Datasource上下文的扩展