依赖注入

以前的JUnit的类构造方法和测试方法都是不能有参数的,JUnit Jupiter有一个颠覆性的改进,就是允许它们有入参,这样就能做依赖注入了。

如果你对pytest的fixture有了解的话,就知道这个技术是多么的强大。

ParameterResolver是一个接口类,类构造方法和测试方法在运行时,必须由被注册的ParameterResolver进行解析。JUnit Jupiter有三个自动注册的内置解析器:

  • TestInfoParameterResolver 参数类型为TestInfo
  • RepetitionInfoParameterResolver 参数类型为RepetitionInfo
  • TestReporterParameterResolver 参数类型为TestReporter

TestInfo

TestInfo包含the display name, the test class, the test method, and associated tags等信息。

示例:

import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertTrue; import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.DisplayName;
import org.junit.jupiter.api.Tag;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.TestInfo; @DisplayName("TestInfo Demo")
class TestInfoDemo { TestInfoDemo(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() {
} }

RepetitionInfo

主要是@RepeatedTest会用到,包含当前重复以及总重复次数等信息。

TestReporter

TestReporter能用来输出额外的信息。

示例:

class TestReporterDemo {

    @Test
void reportSingleValue(TestReporter testReporter) {
testReporter.publishEntry("a status message");
} @Test
void reportKeyValuePair(TestReporter testReporter) {
testReporter.publishEntry("a key", "a value");
} @Test
void reportMultipleKeyValuePairs(TestReporter testReporter) {
Map<String, String> values = new HashMap<>();
values.put("user name", "dk38");
values.put("award year", "1974"); testReporter.publishEntry(values);
} }

传自定义参数

除了内置解析器,如果想传自定义参数,那么需要使用@ExtendWith注册扩展,比如:

@ExtendWith(RandomParametersExtension.class)
class MyRandomParametersTest { @Test
void injectsInteger(@Random int i, @Random int j) {
assertNotEquals(i, j);
} @Test
void injectsDouble(@Random double d) {
assertEquals(0.0, d, 1.0);
} }

有点插件的意思,更常见的是MockitoExtension和SpringExtension。

测试接口

JUnit Jupiter除了测试类和测试方法,其实也有测试接口,比如:

@TestInstance(Lifecycle.PER_CLASS)
interface TestLifecycleLogger { static final Logger logger = Logger.getLogger(TestLifecycleLogger.class.getName()); @BeforeAll
default void beforeAllTests() {
logger.info("Before all tests");
} @AfterAll
default void afterAllTests() {
logger.info("After all tests");
} @BeforeEach
default void beforeEachTest(TestInfo testInfo) {
logger.info(() -> String.format("About to execute [%s]",
testInfo.getDisplayName()));
} @AfterEach
default void afterEachTest(TestInfo testInfo) {
logger.info(() -> String.format("Finished executing [%s]",
testInfo.getDisplayName()));
} }
interface TestInterfaceDynamicTestsDemo {

    @TestFactory
default Stream<DynamicTest> dynamicTestsForPalindromes() {
return Stream.of("racecar", "radar", "mom", "dad")
.map(text -> dynamicTest(text, () -> assertTrue(isPalindrome(text))));
} }

@Test, @RepeatedTest, @ParameterizedTest, @TestFactory, @TestTemplate, @BeforeEach, and @AfterEach能作用到接口的default方法上。

default方法是接口已经实现好了的方法,接口的实现类不需要再编写实现代码,就能直接使用。

如果测试类是@TestInstance(Lifecycle.PER_CLASS)注解,那么可以使用@BeforeAll and @AfterAll。

测试接口可以作为模版。如果测试接口有@ExtendWith and @Tag注解,那么它的实现类也会继承tags and extensions。比如:

@Tag("timed")
@ExtendWith(TimingExtension.class)
interface TimeExecutionLogger {
}
class TestInterfaceDemo implements TestLifecycleLogger,
TimeExecutionLogger, TestInterfaceDynamicTestsDemo { @Test
void isEqualValue() {
assertEquals(1, "a".length(), "is always equal");
} }

结果:

INFO  example.TestLifecycleLogger - Before all tests
INFO example.TestLifecycleLogger - About to execute [dynamicTestsForPalindromes()]
INFO example.TimingExtension - Method [dynamicTestsForPalindromes] took 19 ms.
INFO example.TestLifecycleLogger - Finished executing [dynamicTestsForPalindromes()]
INFO example.TestLifecycleLogger - About to execute [isEqualValue()]
INFO example.TimingExtension - Method [isEqualValue] took 1 ms.
INFO example.TestLifecycleLogger - Finished executing [isEqualValue()]
INFO example.TestLifecycleLogger - After all tests

测试接口也可以作为契约。比如:

public interface Testable<T> {

    T createValue();

}
public interface EqualsContract<T> extends Testable<T> {

    T createNotEqualValue();

    @Test
default void valueEqualsItself() {
T value = createValue();
assertEquals(value, value);
} @Test
default void valueDoesNotEqualNull() {
T value = createValue();
assertFalse(value.equals(null));
} @Test
default void valueDoesNotEqualDifferentValue() {
T value = createValue();
T differentValue = createNotEqualValue();
assertNotEquals(value, differentValue);
assertNotEquals(differentValue, value);
} }
public interface ComparableContract<T extends Comparable<T>> extends Testable<T> {

    T createSmallerValue();

    @Test
default void returnsZeroWhenComparedToItself() {
T value = createValue();
assertEquals(0, value.compareTo(value));
} @Test
default void returnsPositiveNumberWhenComparedToSmallerValue() {
T value = createValue();
T smallerValue = createSmallerValue();
assertTrue(value.compareTo(smallerValue) > 0);
} @Test
default void returnsNegativeNumberWhenComparedToLargerValue() {
T value = createValue();
T smallerValue = createSmallerValue();
assertTrue(smallerValue.compareTo(value) < 0);
} }

实现类:

class StringTests implements ComparableContract<String>, EqualsContract<String> {

    @Override
public String createValue() {
return "banana";
} @Override
public String createSmallerValue() {
return "apple"; // 'a' < 'b' in "banana"
} @Override
public String createNotEqualValue() {
return "cherry";
} }

小结

本文先介绍了JUnit Jupiter的颠覆性技术,允许传参以实现依赖注入,然后介绍了除了测试类和测试方法以外的测试接口,它既可以作为测试模板,也可以作为测试契约。

参考资料:

https://junit.org/junit5/docs/current/user-guide/#writing-tests-dependency-injection

https://junit.org/junit5/docs/current/user-guide/#writing-tests-test-interfaces-and-default-methods

https://blog.csdn.net/qq_35387940/article/details/104767746

JUnit5依赖注入与测试接口的更多相关文章

  1. DDD实战8_2 利用Unity依赖注入,实现接口对应实现类的可配置

    1.在Util类库下新建DIService类 /// <summary> /// 创建一个类,对应在配置文件中配置的DIServices里面的对象的 key /// </summar ...

  2. 在 xunit 测试项目中使用依赖注入

    在 xunit 测试项目中使用依赖注入 Intro 之前写过几篇 xunit 依赖注入的文章,今天这篇文章将结合我在 .NET Conf 上的分享,更加系统的分享一下在测试中的应用案例. 之所以想分享 ...

  3. 再学习之Spring(依赖注入)

    一.概述 Spring框架是以 简化Java EE应用程序的开发 为目标而创建的.Spring可以实现很多功能,但是这些功能的底层都依赖于它的两个核心特性,也就是依赖注入和面向切面编程.几乎Sprin ...

  4. XUnit 依赖注入

    XUnit 依赖注入 Intro 现在的开发中越来越看重依赖注入的思想,微软的 Asp.Net Core 框架更是天然集成了依赖注入,那么在单元测试中如何使用依赖注入呢? 本文主要介绍如何通过 XUn ...

  5. Android 和 Dagger 2 中的依赖注入

    原文:Dependency Injection in Android with Dagger 2 作者:Joe Howard 译者:kmyhy 在现代开发团队中到处充斥着"你一定要用依赖注入 ...

  6. 【10分钟学Spring】:(二)一文搞懂spring依赖注入(DI)

    Spring最基础的特性就是创建bean.管理bean之间的依赖关系.下面通过具体实例演示该如何装配我们应用中的bean. Spring提供了三种主要的装配机制 在xml中进行显示的配置 在Java中 ...

  7. 三大框架 之 Spring(IOC控制反转、DI依赖注入)

    目录 常用词汇 left join与left outer join的区别 Struts2的标签库导入 Spring Spring概述 什么是Spring spring特点 下载 IOC 什么IOC 传 ...

  8. Spring官网阅读(二)(依赖注入及方法注入)

    上篇文章我们学习了官网中的1.2,1.3两小节,主要是涉及了容器,以及Spring实例化对象的一些知识.这篇文章我们继续学习Spring官网,主要是针对1.4小节,主要涉及到Spring的依赖注入.虽 ...

  9. 五:.net core(.NET 6)使用Autofac实现依赖注入

    Autofac的简单使用: 由于将来可能引用很多包,为了保持统一队形,我们再新建一个类库项目Wsk.Core.Package,当做包的引用集合: 删掉Class1,把Wsk.Core.Wsk.Core ...

随机推荐

  1. 密码学系列之:碰撞抵御和碰撞攻击collision attack

    密码学系列之:碰撞抵御和碰撞攻击collision attack 简介 hash是密码学和平时的程序中经常会用到的一个功能,如果hash算法设计的不好,会产生hash碰撞,甚至产生碰撞攻击. 今天和大 ...

  2. NVIDIA GPU上的Tensor线性代数

    NVIDIA GPU上的Tensor线性代数 cuTENSOR库是同类中第一个GPU加速的张量线性代数库,提供张量收缩,归约和逐元素运算.cuTENSOR用于加速在深度学习训练和推理,计算机视觉,量子 ...

  3. CodeGen结构循环回路

    CodeGen结构循环回路 structure循环是一个模板文件构造,它允许您迭代CodeGen拥有的有关结构的集合.为了使用结构循环,必须同时基于多个存储库结构生成代码. CodeGen可以通过以下 ...

  4. NX二次开发-将3X3矩阵修正为正交且长度为单位长度的矩阵

    函数:UF_MTX3_ortho_normalize() 函数说明:将矩阵修正为正交且xyz长度为单位长度的矩阵.下图中输入的矩阵为三条线段的端点,经过修正后,生成一个坐标系. 1 #include ...

  5. C# Net Core 使用 itextsharp.lgplv2.core 把Html转PDF

    C# Net Core 使用 itextsharp.lgplv2.core 把Html转PDF 只支持英文(中文我不知道怎么弄,懂的朋友帮我看一下)!!!!![补充:评论区的小伙伴已解决] 引入包it ...

  6. Java中JVM、JRE和JDK三者有什么区别和联系?

    Java 语言的开发运行,也离不开 Java 语言的运行环境 JRE.没有 JRE 的支持,Java 语言便无法运行.当然,如果还想编译 Java 程序,搞搞小开发的话,JRE 是明显不够了,这时候就 ...

  7. JNI相关笔记 [TOC]

    JNI相关笔记 目录 JNI相关笔记 1 生成native code所需要的头文件 2 JNI提供的一些函数和方法 3 局部引用,全局引用,全局弱引用. 4 异常 1 生成native code所需要 ...

  8. ffmpeg入门到实战-ffmpeg是怎么转码的?

    阅读目录 视频是怎么被播放的? ffmpeg命令的格式 ffmpeg转码输出的过程 视频是怎么被播放的? 我们知道,当下大多数播放器都是基于ffmpeg二次开发的.你有没有想过,你用播放器打开一个视频 ...

  9. npm ERR! Unexpected end of JSON input while parsing near '...'解决方法

    npm install时出现npm err! Unexpected end of JSON input while parsing near'...'错误 输入  npm cache clean -- ...

  10. .net core 支付宝,微信支付 一

    源码: https://github.com/aspros-luo/Qwerty.Payment/tree/develop 支付宝支付:参考支付宝sdk及文档,https://docs.open.al ...