JUnit 5 –动态测试

在定义测试时,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

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

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

相关文章

python基础-网络基础知识和网络编程

之前对这一块的知识,总是记不住,这次正好有系统的学习,所以决定好好的梳理一下 1. 计算机网络基础知识 1.1 互联网协议和OSI模型 *协议模型互联网协议按照功能不同分为osi七层或tcp/ip五层或tcp/ip四层,如下图所示 *每层运行常见物理设备 *每层运行常见的协议 1.2 基础网络概念…

LeetCode(90):子集 II

Medium&#xff01; 题目描述&#xff1a; 给定一个可能包含重复元素的整数数组 nums&#xff0c;返回该数组所有可能的子集&#xff08;幂集&#xff09;。 说明&#xff1a;解集不能包含重复的子集。 示例: 输入: [1,2,2] 输出: [[2],[1],[1,2,2],[2,2],[1,2],[] ] 解题思路&…

一念成佛,一念成魔

一念成佛&#xff0c;一念成魔 前几天&#xff0c;因我与寝友的一次错误导致电脑系统崩溃&#xff0c;连开机都开不了了。可我发现我并不是很生气&#xff0c;只是用玩笑话调侃了他一下。后来想想&#xff0c;要是放在以前&#xff0c;我一定会大发脾气&#xff0c;几天都不去理…

nodejs微信小程序+python+PHP计算机网络在线考试系统-计算机毕业设计推荐

目 录 摘 要 I ABSTRACT II 目 录 II 第1章 绪论 1 1.1背景及意义 1 1.2 国内外研究概况 1 1.3 研究的内容 1 第2章 相关技术 3 2.1 nodejs简介 4 2.2 express框架介绍 6 2.4 MySQL数据库 4 第3章 系统分析 5 3.1 需求分析 5 3.2 系统可行性分析 5 3.2.1技术可行性&#xff1a;…

组件模块化使用

// 使用$attr可以直接在父元素上拿数据让子元素使用或者是孙元素使用 <template><div>props:{{name}},{{age}} 或者 {{$props[name]}},{{$props[age]}} <br>attrs: {{$attrs[gender]}} 在$attrs里面只会有props没有注册的属性</div> </template>…

VS2010发布、打包安装程序超全超详细

找不到原文链接 1、 在vs2010 选择“新建项目”“其他项目类型”“ Visual StudioInstaller “安装项目”&#xff1a; 命名为&#xff1a;Setup1 。 这是在VS2010中将有三个文件夹&#xff0c; 1.“应用程序文件夹”表示要安装的应用程序需要添加的文件&#xff1b; 2.“…

介绍HawkFX

如前所述&#xff0c; 我开始玩JRubyFX 。 对我来说&#xff0c;学习一些新的最佳方法可以解决用例&#xff0c;因此我开始为Hawkular创建库存浏览器。 为什么选择JRubyFX&#xff1f; 让我们首先从“什么是JRubyFX”开始&#xff1f; JRubyFX是通过JRuby引入Ruby世界的JavaFX…

HibernateTemplate

org.springframework.orm.hibernate5.HibernateTemplate 这是spring-orm-4.3.4.RELEASE.jar包中的一个类&#xff0c;这个类是对Hibernate进行了封装&#xff1b; 这是可以进行注入的属性&#xff0c;需要注入sessionFactory属性&#xff0c;因此我们需要创建一个sessionFactor…

第四章: 4.1 logging模块 | 正则表达式

修改json数据然后写入json文件中 f open(1234.json,r,encodingutf-8) data f.read() data1 json.loads(data)data1[status] 1f1 open(1234.json,w,encodingutf-8)json.dump(data1,f1) hashlib md5值的用法 #加入下面这个就可以 password input(请输入密码:)m hashlib.md…

使用保险柜管理机密

您如何存储秘密&#xff1f; 密码&#xff0c;API密钥&#xff0c;安全令牌和机密数据属于秘密类别。 那是不应该存在的数据。 在容易猜测的位置&#xff0c;不得以纯文本格式提供。 实际上&#xff0c;不得在任何位置以明文形式存储它。 可以使用Spring Cloud Config Server或…

winScp中文乱码设置

打开一个putty窗口(图一)&#xff0c;左上角鼠标左键点击&#xff0c;弹出设置界面&#xff0c;选择Change Settings&#xff0c;在图二界面的window->translation&#xff0c;Remote character set选择UTF-8&#xff0c;Apply应用即可。 转载于:https://www.cnblogs.com/ya…

多台电脑共用一个耳机、音箱

台式机电脑声卡一般有三个插孔&#xff0c;一个是麦克风&#xff0c;一个是耳机&#xff0c;另一个就是LineIn输入口了&#xff0c;买一根AUX线&#xff0c;一头插入笔记本的耳机插口&#xff0c;另一头插入台式机linein口&#xff1b;在控制面板的声音中选择线路输入&#xff…

liferay开发文档_Liferay –简单主题开发

liferay开发文档实际上&#xff0c;Liferay的6.1版本已经走了很长一段路&#xff0c;该版本完全支持JSF和IceFaces。 我一直在努力学习它的绳索&#xff0c;因为我希望使其成为我们团队中的标准协作工具。 好的软件应用程序可以解决问题&#xff0c;但是好的软件应用程序不仅可…

ACRush 楼天城回忆录

利用假期空闲之时&#xff0c;将这几年 GCJ &#xff0c; ACM &#xff0c; TopCoder 参加的一些重要比赛作个回顾。首先是 GCJ2006 的回忆。 Google Code Jam 2006 一波三折&#xff1a; Google Code Jam 2006 是我第一次到美国参加现场的程序设计比赛。 Google Code Jam 2006…

JUnit 5 –条件

最近&#xff0c;我们了解了JUnit的新扩展模型以及它如何使我们能够将自定义行为注入测试引擎。 我向你保证要看情况。 现在就开始吧&#xff01; 条件允许我们在应该执行或不应该执行测试时定义灵活的标准。 它们的正式名称是“ 条件测试执行” 。 总览 本系列中有关JUnit 5…

我的编程竞赛之路 ——中国大学生计算机编程第一人楼天城访谈

记者/陈秋歌 25岁的楼天城有“中国大学生计算机编程第一人”的称号&#xff0c;也被参加竞赛的学子们敬称为“楼教主”。他的传奇经历一直激励着众多年轻学子&#xff1a;从2001年开始参加计算机编程竞赛&#xff0c;并连获全国一等奖&#xff1b;2004年入选国家集训队&#xf…

jenkins部署_Jenkins:部署JEE工件

jenkins部署随着持续集成和持续交付的出现 &#xff0c;我们的构建被分为不同的步骤&#xff0c;以创建部署管道。 这些步骤中的一些步骤可以是例如编译和运行快速测试&#xff0c;运行慢速测试&#xff0c;运行自动验收测试或发布应用程序等。 部署流程的最后步骤意味着将我们…

2013年3月编程语言排行榜:有毒的Java

2013年3月12日&#xff0c;Tiobe公布了新一期编程语言排行榜。Java依旧是占据第一的位置&#xff0c;C语言紧随其后。值得注意的Objective-C持续发力&#xff0c;已经占到了第三的位置。咋一看榜单&#xff0c;前5条中C#下滑最快&#xff0c;从第3名下降到第五名。而其他语言都…

DHCP服务(dhcpd)

DHCP动态分配主机地址&#xff08;Dynamic Host Configuration Protocol&#xff09; 动态主机配置协议&#xff08;DHCP&#xff09;是一种基于UDP协议且仅限于在局域网内部使用的网络协议&#xff0c;主要用于大型的局域网环境或者存在较多移动办公设备的局域网环境中&#x…

基于Matlab/Simulink不平衡电网工况下级联H桥光伏并网逆变器仿真模型

本次更新的内容为级联H桥光伏并网逆变器相关的控制&#xff0c;后面会针对储能系统在级联H桥拓扑上的应用进行分享。由于传统发电造成的环境污染问题和光伏电池板价格持续创新低&#xff0c;太阳能从众多种类的可再生能源中拔地而起&#xff0c;因而光伏逆变器成为国内外学者和…