junit5 动态测试_JUnit 5 –动态测试

junit5 动态测试

在定义测试时,JUnit 4有一个很大的弱点:它必须在编译时发生。 现在,JUnit 5将解决此问题! Milestone 1 刚刚发布 ,它带有全新的动态测试,可以在运行时创建测试。

总览

本系列中有关JUnit 5的其他文章:

  • 建立
  • 基本
  • 建筑
  • 扩展模型
  • 条件
  • 注射
  • 动态测试

本系列基于预发行版本Milestone 1 ,当然会随时更改。 发布新的里程碑或常规发布版本时,帖子将更新。


您将在此处阅读的大多数内容和更多内容都可以在新兴的JUnit 5用户指南中找到 (该链接已链接到Milestone 1版本–您可以在此处找到最新版本)。 我在这里显示的代码示例可以在GitHub上找到 。

静态测试

JUnit 3通过分析方法名称并检查它们是否以测试开头来识别测试。 JUnit 4利用了(后来的)注解,并引入了@Test,它给了我们更多的自由。 这两种技术共享相同的方法:测试是在编译时定义的。

但是,事实证明这是相当有限的。 例如,考虑一种常见情况,即应该对多种输入数据执行相同的测试,在这种情况下,应针对许多不同点:

void testDistanceComputation(Point p1, Point p2, double distance) {assertEquals(distance, p1.distanceTo(p2));
}

我们有什么选择? 最直接的方法是创建许多有趣的点,然后在循环中调用我们的测试方法:

@Test
void testDistanceComputations() {List<PointPointDistance> testData = createTestData();for (PointPointDistance datum : testData) {testDistanceComputation(datum.point1(), datum.point2(), datum.distance());}
}

但是,如果这样做,JUnit会将循环视为单个测试。 这意味着测试仅在第一个失败之前执行,报告会受到影响,并且工具支持通常不及格。

有几个JUnit 4功能和扩展可解决此问题。 它们或多或少都可以工作,但通常限于特定的用例( Theories ),使用起来很笨拙( Parameterized ),并且通常需要运行程序(值得称赞的JUnitParams )。 原因是它们都受到相同的限制:JUnit 4确实不支持在运行时创建测试。

使用lambda创建测试也是如此。 有些人想定义这样的测试:

class PointTest {"Distance To Origin" -> {Point origin = Point.create(0,0);Point p = Point.create(3,4);assertEquals(5, origin.distanceTo(p));}}

当然,这只是一个理想选择-它甚至无法在Java中进行编译。 尽管如此,看到我们能达到多近还是很有趣的。 las,也无法静态标识各个lambda,因此此处也有相同的限制。

但是,如果JUnit 5没有提出解决方案,我不会写所有这些内容:动态测试进行救援!
junit-5-dynamic-tests

发布时间由NASA戈达德太空飞行中心在CC-BY-SA 2.0

动态测试

从最近开始,JUnit 5代码库就采用了新的类型和新的注释,它们共同解决了我们的问题。

首先,有DynamicTest ,它是测试的简单包装。 它有一个名称,并保存构成测试主体的代码。 后者以Executable的形式发生,就像Runnable但是可以抛出任何Throwable (可塑命名)。 它是使用静态工厂方法创建的:

public static DynamicTest dynamicTest(String name, Executable test);

然后是@TestFactory ,可以注释方法。 这些方法必须返回动态测试的IteratorIterableStream 。 (这当然不能在编译时强制执行,因此,如果我们返回其他内容,JUnit将在运行时发出barf。)

很容易看出他们是如何合作的:

  1. 当寻找@Test方法时,JUnit还将发现@TestFactory方法。
  2. 在构建测试树时,它将执行这些方法并将生成的测试添加到树中。
  3. 最终,将执行测试。

因此,我们能够在运行时动态创建测试:

@TestFactory
List<DynamicTest> createPointTests() {return Arrays.asList(DynamicTest.dynamicTest("A Great Test For Point",() -> {// test code}),DynamicTest.dynamicTest("Another Great Test For Point",() -> {// test code}));
}

让我们看看如何使用它来解决我们上面描述的问题。

要创建参数化测试,我们执行与之前非常相似的操作:

@TestFactory
Stream<DynamicTest> testDistanceComputations() {List<PointPointDistance> testData = createTestData();return testData.stream().map(datum -> DynamicTest.dynamicTest("Testing " + datum,() -> testDistanceComputation(datum.point1(), datum.point2(), datum.distance())));
}

与上面所做的操作的关键区别在于,我们不再直接执行testDistanceComputation 。 取而代之的是,我们为每个数据创建一个动态测试,这意味着JUnit将知道这些测试很多,而不仅仅是一个。

在这种情况下,我们可能会使用其他方法来生成动态测试:

@TestFactory
Stream<DynamicTest> testDistanceComputations() {return DynamicTest.stream(createTestData().iterator(),datum -> "Testing " + datum,datum -> testDistanceComputation(datum.point1(), datum.point2(), datum.distance()));
}

在这里,我们将测试数据传递给stream ,然后告诉它如何从中创建名称和测试。

所以你怎么看? 也许符合“ JUnit 5将这些作为单独的测试来对待,但是从语法上来看仍然很麻烦”的思路吗? 好吧,至少我是这样认为的。 该功能很好,但有点笨拙。

但这只是里程碑1,因此有足够的时间进行改进。 也许扩展可以提供一种更舒适的方式来创建动态测试,但是我不太清楚如何。 我想, 新的扩展点会有所帮助。

Lambda测试

好吧,让我们看看我们距离备受追捧的lambda测试有多近。 现在,并未为此明确创建动态测试,因此我们必须进行一些修改。 (这种修补是由Jens Schauder 关于JUnit 5的演讲之一“错误地启发”的。感谢Jens!)

动态测试需要一个名称和一个可执行文件,用lambda创建后者听起来很合理。 为了能够做到这一点,我们需要一个目标,即lambda被分配给的目标。 想到一个方法参数...

但是该方法会做什么? 显然,它应该创建一个动态测试,然后呢? 也许我们可以将该测试转储到某个地方,然后让JUnit进行测试?

public class LambdaTest {private final List<DynamicTest> tests = new ArrayList<>();// use lambda to create the 'Executable'public void registerTest(String name, Executable test) {tests.add(DynamicTest.dynamicTest(name, test));}@TestFactoryvoid List<DynamicTest> tests() {return tests;}}

好的,这看起来很有希望。 但是,我们从哪里获得LambdaTest的实例? 对于我们的测试类,最简单的解决方案是简单地对其进行扩展,然后重复调用registerTest 。 如果这样做的话,我们可能更喜欢一个短名称。 我们还可以使其受到保护:

// don't do this at home!
protected void λ(String name, Executable test) {tests.add(DynamicTest.dynamicTest(name, test));
}

看来我们要到达那里。 剩下的就是调用λ了,唯一显而易见的方法是从测试类的构造函数内部进行:

class PointTest extends LambdaTest {public PointTest() {λ("A Great Test For Point", () -> {// test code})}}

我们已经完成修补工作。 为了进一步发展,我们必须开始黑客攻击。 有没有听说过双括号初始化 ? 这是一个有点奇怪的功能,它创建一个匿名子类并在新类的构造函数中执行给定的代码。 有了它,我们可以走得更远:

class PointTest extends LambdaTest {{λ("A Great Test For Point", () -> {// test code});}}

如果我们真的很渴望,我们可以删除另外两个符号。 有了这个怪异的技巧 (我们现在受到Benji Weber的启发),我们可以通过反射确定lambda的参数名称,并将其用作测试的名称。 为了利用这一点,我们需要一个新的接口,并且必须稍微更改LambdaTest ::λ:

@FunctionalInterface
// the interface we are extending here allows us
// to retrieve the parameter name via 'prettyName()'
// (the black magic is hidden inside that method;
//  look at 'MethodFinder' and 'NamedValue' in Benji's post)
public interface NamedTest extends ParameterNameFinder {void execute(String name);
}protected void λ(NamedTest namedTest) {String name = namedTest.prettyName();Executable test = () -> namedTest.execute(name);tests.add(DynamicTest.dynamicTest(name, test));
}

总而言之,我们可以创建如下测试:

class PointTest extends LambdaTest {{λ(A_Great_Test_For_Point -> {// test code});}}

你怎么看? 所有这些黑客值得吗? 老实说,我不介意让我的IDE生成测试方法样板,所以我的回答是“否”。 但这是一个有趣的实验。 :)

生命周期

动态测试的当前实现是故意的。 这种显示方式之一是它们没有集成到生命周期中。 从用户指南中:

这意味着对于动态测试,不会执行@BeforeEach和@AfterEach方法及其对应的扩展回调。 换句话说,如果您在lambda表达式中访问来自测试实例的字段以进行动态测试,则这些字段将不会由回调方法或由同一@TestFactory方法生成的动态测试在执行之间的扩展名进行重置。

不过,已经有一个问题可以解决 。

反射

那我们看到了什么? 到目前为止,JUnit只知道在编译时声明的测试。 JUnit 5具有动态测试的概念,动态测试是在运行时创建的,由名称和保存测试代码的可执行文件组成。 到此为止,我们已经看到了如何创建参数化测试以及如何使用lambda来以更现代的风格定义测试。

你怎么看? 渴望尝试吗?

翻译自: https://www.javacodegeeks.com/2016/07/junit-5-dynamic-tests.html

junit5 动态测试

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/335845.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

C++ 11 深度学习(十)原始字面量

你是否曾经为了各种json格式无法写入string中而烦恼&#xff0c;为了各种转义而烦恼。如下图 c11为我们带来了全新的解决方法 其新特性为使用. R"(xxxxxxxxxxxx)" ,此种形式可以使得以原有形式进行表现出来

交流伺服系统设计指南_交流设计

交流伺服系统设计指南软件设计至关重要。 它是应用程序的基础。 就像蓝图一样&#xff0c;它为所有背景的聚会提供了一个通用平台。 它有助于理解&#xff0c;协作和发展。 设计不应仅视为开发的要素。 它不应该仅仅存在于开发人员的脑海中&#xff0c;否则团队将发现它几乎无…

maven 父maven_Maven神秘化

maven 父maven由于我的Android开发的背景下&#xff0c;我比较习惯到Gradle &#xff0c;而不是Maven的 。 尽管我知道Gradle基于Maven&#xff0c;但我从未调查过幕后发生的事情。 在过去的一周中&#xff0c;我一直在尝试了解细节并找出Maven的不同组成部分。 什么是Maven M…

【WebRTC---序篇】(一)为什么要使用WebRTC

1.1.1自研直播客户端架构 一个最简单的直播客户端至少应该包括音视频采集模块,音视频编码模块,网络传输模块,音视频解码模块和音视频渲染模块五大部分。如下图所示 1.1.2拆分音视频模块 在实际开发中,音频和视频处理完全是独立的。如下图所示,经过细分后,音频采集与视频…

DFS深搜与BFS广搜专题

一般搜索算法的流程框架 DFS和BFS与一般搜索流程的关系 如果一般搜索算法流程4使用的是stack栈结构(先进后出&#xff0c;后进先出)那么就会越搜越深。即&#xff0c;DFS&#xff0c;DFS只保存当前一条路径&#xff0c;其目的是枚举出所有可能性。反之&#xff0c;如果流程4使…

cloud foundry_使用“另类” Cloud Foundry Gradle插件无需停机

cloud foundry我一直在尝试编写用于将应用程序部署到Cloud Foundry的gradle插件 &#xff0c;并在上一篇文章中写了有关此插件的文章 。 现在&#xff0c;我通过使用两种方法支持将无停机时间部署到Cloud Foundry中来增强此插件&#xff1a; 自动驾驶风格部署和更常用的蓝绿色风…

懒惰学习_懒惰评估

懒惰学习最近&#xff0c;我正在编写log4j附加程序&#xff0c;并希望在自定义附加程序创建过程中使用logger记录一些诊断详细信息&#xff0c;但是log4j初始化仅在创建附加程序实例后才完成&#xff0c;因此在此阶段记录的消息将被忽略。 我感到需要在自定义附加程序中进行延…

leetcode(动态规划专题)

线性DP 53. 最大子数组和 思路 code int maxSubArray(vector<int>& nums) {//res:最后所有状态的最终Max结果//lat:当前f[i]状态的Maxint res INT_MIN, last 0;for (int i 0; i < nums.size(); i){//当前f[i]状态最大值(使用下面的状态转移方程得出)//f[i] …

leetcode(链表专题)

数组模拟链表 #include<iostream> using namespace std;const int N 100; // 单链表 // head存储链表头&#xff0c;e[]存储节点的值&#xff0c;ne[]存储节点的next指针&#xff0c;idx表示当前用到了哪个节点 int head, e[N], ne[N], idx;// 初始化 void init() {hea…

lagom cqrs_Java和Lagom的CQRS

lagom cqrs我很高兴在Chicago Java User Group上进行了讨论&#xff0c;并讨论了Lagom如何实现CQRS&#xff08;命令查询责任隔离模式&#xff09;。 值得庆幸的是&#xff0c;有一个录音&#xff0c;我还把这些幻灯片发布在slideshare上 。 抽象&#xff1a; 一旦应用程序变…

【WebRTC---源码篇】(四)WebRTC线程模型

常见的线程模型 1.为了解决频繁线程创建与销毁,在此模型中使用的线程池。在线程池创建的时候就将一些线程创建起来,以提高效率。通过控制线程数量来解决线程频繁切换。 2.一般线程与线程存在前后关系的,线程执行完毕之后生成一个新的任务(task1 , task2,task3---)插入到任…

java cuba_CUBA平台–用于快速应用程序开发的开源Java框架

java cuba传统上&#xff0c;自计算时代开始以来&#xff0c;企业软件开发自然面临着一个挑战&#xff0c;当时自然而然地&#xff0c;企业软件开发本应专注于解决实际的业务问题&#xff0c;但与此同时&#xff0c;开发人员必须在技术上花费大量时间和精力。解决方案的一面&am…

java中什么时候应用异常_生产Java应用程序中的十大异常类型-基于1B事件

java中什么时候应用异常Pareto记录原理&#xff1a;97&#xff05;的记录错误语句是由3&#xff05;的唯一错误引起的 在最新的数据整理帖子之后&#xff0c;我们收到了很多反馈和问题&#xff0c;我们发现97&#xff05;的记录错误是由10个唯一错误引起的 。 根据大众的需求&…

C++ 11 深度学习(十四)C++类

(一)综述&#xff1a;类是我们自己定义的数据类型 设计时要考虑的角度&#xff1a; 站在设计和实现的角度来考虑&#xff1b;站在使用者的角度考虑&#xff1b;父类&#xff0c;子类之间的考虑&#xff1b; &#xff08;二&#xff09;explicit 首先, C中的explicit关键字只…

手动编译 lombok_Lombok,一种编译时Java注释预处理器,可最大程度地减少代码大小...

手动编译 lombok在本文中&#xff0c;我们将看到如何在常规Java代码中使用lombok来最大程度地减少代码长度和冗余。 什么是Lombok&#xff1f; Lombok&#xff0c;一个编译时注释预处理器&#xff0c;有助于在编译时注入一些代码。 在详细介绍之前&#xff0c;我要求您应该从…

mysql超大表处理方式是_第29问:MySQL 的复制心跳说它不想跳了

问题最近年底&#xff0c;大家的数据库经常跑批量大事务&#xff0c;会发现复制突然断开&#xff0c;报错“心跳与本地信息不兼容”&#xff1a;会是什么原因&#xff1f;实验我们先来复现一下&#xff0c;再进行分析。宽油&#xff0c;做一对主从数据库&#xff1a;我们先造一…

sap 分摊分配不产生会计凭证的原因_SAP软件的物料分类账功能

物料账介绍物料分类账(Material Ledger)是SAP财务模块的重要功能之一&#xff0c;用于对标准价计价的物料进行差异处理。激活ML后&#xff0c;系统会在"工厂物料"层(或更细的层次)为每个物料建立子账簿&#xff0c;记录该物料在各个期间的数量、价值变化情况。月底运…

忽略异常_忽略已检查的异常,所有出色的开发人员都在这样做–基于600,000个Java项目...

忽略异常Github和Sourceforge上超过600,000个Java项目中的异常处理概述 Java是使用检查异常的少数几种语言之一。 它们在编译时强制执行&#xff0c;并且需要某种处理。 但是……在实践中会发生什么&#xff1f; 大多数开发人员实际上会处理任何事情吗&#xff1f; 他们怎么做…

数据结构【队列专题】

先进先出&#xff08;First In First Out&#xff0c;FIFO&#xff09;的线性序列&#xff0c;成为“队列”。 队列也是一种线性表&#xff0c;只不过它是操作受限的线性表&#xff0c;只能在两端操作&#xff1a; 一端进&#xff0c;一端出。进的一端成为队尾&#xff08;re…

oracle安装卡在create inventory_滴滴云服务器上安装Oracle12cR2单实例数据库

一、相关说明Oracle数据库是由美国甲骨文公司推出的一款优秀的关系型数据库。当前最新版为Database 19c。本文简述Oracle Database 12cR2在滴滴云主机上的安装过程。如需更权威的指导方法请参考Oracle官方安装文档。二、环境概述一台DC2云主机&#xff08;2c4g&#xff09;一个…