如何在JUnit 5中替换规则

最近发布的JUnit 5(又名JUnit Lambda) Alpha版本引起了我的兴趣,在浏览文档时,我注意到规则以及跑步者和阶级规则都消失了。 根据文档,这些部分竞争的概念已被单个一致的扩展模型取代。

多年来, Frank和我写了一些规则来帮助执行重复性任务,例如测试SWT UI , 忽略某些环境中的测试 , 注册(测试)OSGi服务 , 在单独的线程中运行测试等等。

因此,我对将现有规则转换为新概念以使它们可以在JUnit 5上本地运行特别感兴趣。为了探索扩展的功能,我选择了两个特性完全不同的规则,并尝试将它们迁移到JUnit 5 。

这些实验的重点是查看规则和扩展之间的概念已发生了变化。 因此,我选择重写JUnit 4意味着不考虑向后兼容性。

如果您有兴趣从JUnit 4迁移到5或探索在JUnit 5中运行现有规则的可能性,则可能需要参加相应的讨论。

第一个候选对象是ConditionalIgnoreRule ,它与@ConditionalIgnore批注一起使用。 该规则评估需要用注释指定的条件,并据此确定是否执行测试。

另一个候选者是内置的TemporaryFolder规则 。 顾名思义,它允许创建在测试完成时删除的文件和文件夹。

因此,它在测试执行之前和之后挂接,以创建一个根目录以在其中存储文件和文件夹并清理该目录。 此外,它提供了实用程序方法来在根目录中创建文件和文件夹。

扩展说明

在详细介绍向扩展的迁移规则之前,让我们简要了解一下新概念。

junit5扩展点类型层次结构

测试执行遵循一定的生命周期。 可以延长生命周期的每个阶段都由一个接口表示。 扩展可以在某些阶段表达兴趣,因为它们实现了相应的接口。

使用ExtendWith批注,测试方法或类可以表示它在运行时需要特定的扩展。 所有扩展都有一个公共的超级接口: ExtensionPointExtensionPoint的类型层次结构列出了扩展当前可以挂接到的所有位置。

例如,下面的代码应用了一个虚构的MockitoExtension来注入模拟对象:

@ExtendWith(MockitoExtension.class)
class MockTest {@MockFoo fooMock; // initialized by extension with mock( Foo.class )
}

MockitoExtension将提供一个默认的构造函数,以便可以在运行时实例化它,并实现必要的扩展接口,以便能够将@Mock注入到所有@Mock注释的字段中。

条件忽略

规则的重复模式是提供带有注释的串联服务,该注释用于标记和/或配置希望使用该服务的测试方法。 在这里,ConditionalIgnoreRule检查其运行的所有测试方法,并寻找ConditinalIgnore批注。 如果找到了这样的注释,则评估其条件,如果满足,则忽略测试。

这是ConditionalIgnoreRule实际运行的样子:

@Rule
public ConditionalIgnoreRule rule = new ConditionalIgnoreRule();@Test
@ConditionalIgnore( condition = IsWindowsPlatform.class )
public void testSomethingPlatformSpecific() {// ...
}

现在,让我们看一下代码在JUnit 5中的外观:

@Test
@DisabledWhen( IsWindowsPlatform.class )
void testSomethingPlatformSpecific() {// ...
}

首先,您会注意到注释已更改其名称。 为了匹配使用术语Disabled而不是被忽略的JUnit 5约定,该扩展还将其名称更改为DisabledWhen

尽管DisabledWhen注释是由DisabledWhenExtension驱动的,但是没有任何东西表明需要扩展。 其原因被称为元注释,并且在查看DisabledWhen的声明方式时可以最好地说明它们:

@Retention(RetentionPolicy.RUNTIME)
@ExtendWith(DisabledWhenExtension.class)
public @interface DisabledWhen {Class<? extends DisabledWhenCondition> value();
}

注释(元)带有处理它的扩展名。 并且在运行时,JUnit 5测试执行器负责其余的工作。 如果遇到带注释的测试方法,并且该注释又由ExtendWith元注释,则实例化各个扩展并将其包含在生命周期中。

真的很整洁吗? 在不指定相应规则的情况下注释测试方法时,此技巧还可以避免疏忽。

在幕后, DisabledWhenExtension实现了TestExexutionCondition接口。 对于每个测试方法,都将调用其唯一的evaluate()方法,并且必须返回一个ConditionEvaluationResult ,该ConditionEvaluationResult确定是否应该执行测试。

其余代码与以前基本相同。 查找并发现DisabledWhen批注时,将创建指定条件类的实例,并询问是否应执行测试。 如果执行被拒绝,则返回一个禁用的ConditionEvaluationResult ,并且框架将采取相应措施。

临时文件夹

在将TemporaryFolder规则变为异常之前,让我们看一下该规则的组成。 首先,该规则将在测试设置和拆卸期间设置并清理一个临时文件夹。 但是,它还为测试提供了访问在该根文件夹内创建(临时)文件和文件夹的方法的权限。

迁移到扩展后,不同的职责变得更加明显。 以下示例显示了如何使用它:

@ExtendWith(TemporaryFolderExtension.class)
class InputOutputTestprivate TemporaryFolder tempFolder;@Testvoid testThatUsesTemporaryFolder() {F‌ile f‌ile = tempFolder.newF‌ile();// ...}
}

TemporaryFolderExtension挂接到测试执行生命周期中,以提供和清除临时文件夹,并为所有TemporaryFolder字段提供此类实例。 而TemporaryFolder允许访问在根文件夹中创建文件和文件夹的方法。

为了注入TemporaryFolder ,该扩展实现了InstancePostProcessor接口。 创建测试实例后立即调用其postProcessTestInstance方法。 在该方法中,它可以通过TestExtensionContext参数访问测试实例,并且可以将TemporaryFolder注入所有匹配的字段中。

对于一个类声明多个TemporaryFolder字段的极少数事件,每个字段都被分配一个新实例,并且每个实例都有其自己的根文件夹。

在此过程中创建的所有注入的TemporaryFolder实例都保存在一个集合中,以便稍后进行清理时可以对其进行访问。

要在执行测试后进行清理,需要实现另一个扩展接口: AfterEachExtensionPoint 。 每次测试完成后,将调用其唯一的afterEach方法。 并且此处的TemporaryFolderExtension实现清除所有已知的TemporaryFolder实例。

现在我们可以与TemporaryFolder规则的功能相提并论,现在还可以支持新功能:方法级依赖注入。
在JUnit 5中,现在允许方法具有参数。
这意味着我们的扩展程序不仅应该能够注入字段,而且还应该能够注入TemporaryFolder类型的方法参数。 希望创建临时文件的测试可以请求注入TemporaryFolder如以下示例所示:

class InputOutputTest {@Test@ExtendWith(TemporaryFolderExtension.class)void testThatUsesTemporaryFolder( TemporaryFolder tempFolder ) {F‌ile f‌ile = tempFolder.newF‌ile();// ...}
}

通过实现MethodParameterResolver接口,扩展可以参与解析方法参数。 对于测试方法的每个参数,都会调用扩展的supports()方法来确定它是否可以为给定参数提供值。 对于TemporaryFolderExtension ,实现将检查参数类型是否为TemporaryFolder并在这种情况下返回true 。 如果需要更广泛的上下文,则当前方法调用上下文和扩展上下文还提供了supports()方法。

现在,该扩展程序决定支持某个参数,它的resolve()方法必须提供一个匹配的实例。 同样,提供了周围的环境。 TemporaryFolderExtension只是返回一个唯一的TemporaryFolder实例,该实例知道(临时)根文件夹并提供在其中创建文件和子文件夹的方法。

但是请注意,声明无法解析的参数被视为错误。 因此,如果遇到没有匹配解析器的参数,则会引发异常。

在扩展中存储状态

您可能已经注意到, TemporaryFolderExtension保持其状态(即,它已创建的临时文件夹的列表),当前是一个简单字段。 尽管测试表明这确实可行,但是文档中没有地方指出在调用不同扩展名时都使用同一实例。 因此,如果JUnit 5此时更改其行为,则在这些调用期间状态可能会丢失。

好消息是,JUnit 5提供了一种维护称为Store的扩展状态的方法。 如文档所述,它们为扩展提供了保存和检索数据的方法

该API与简化Map相似,并且允许存储键值对,获取与给定键关联的值以及删除给定键。 键和值都可以是任意对象。 可以通过将TestExtensionContext作为参数传递给每个扩展方法(例如, beforeEachafterEach )来到达存储。每个TestExtensionContext实例都封装了正在执行当前测试的上下文

例如,在beforeEach ,值将存储在扩展上下文中,如下所示:

@Override
public void beforeEach( TestExtensionContext context ) {context.getStore().put( KEY, ... );
}

以后可以像这样检索:

@Override
public void afterEach( TestExtensionContext context ) {Store store = context.getStore();Object value = store.get( KEY );// use value...
}

为了避免可能发生的名称冲突,可以为某些命名空间创建存储。 上面使用的context.getStore()方法获取默认名称空间的存储。 要获取特定命名空间的存储,请使用

context.getStore( Namespace.of( MY, NAME, SPACE );

名称空间是通过对象数组{ MY, NAME, SPACE }来定义的。

返还TemporaryFolderExtension以使用Store的练习留给读者。

运行代码

  • 可以在以下GitHub存储库中找到此处讨论的两个扩展的尖峰实现: https : //github.com/rherrmann/junit5-experiments

该项目设置为在安装了Maven支持的Eclipse中使用。 但是在具有Maven支持的其他IDE中编译和运行代码并不难。

很自然,在这种早期状态下,尚不支持直接在Eclipse中运行JUnit 5测试。 因此,要运行所有测试,可能需要使用“使用ConsoleRunner运行所有测试”启动配置。 如果遇到麻烦,请参考我以前关于JUnit 5的文章中的“ 使用JUnit 5运行测试”部分,以获得更多提示或发表评论。

总结如何在JUnit 5中替换规则

在这个小小的实验过程中,我给人的印象是,扩展是JUnit 4中规则和朋友的完美替代品。最后,使用新方法很有趣,并且比现有功能更简洁。

如果您发现用扩展尚无法完成的用例,我相信如果让他们知道 JUnit 5团队将不胜感激。

但请注意,在撰写本文时,扩展程序正在进行中 。 该API被标记为实验性的,如有更改,恕不另行通知。 因此,现在实际迁移JUnit 4帮助程序可能还为时过早-除非您不介意将代码调整为可能更改的API。

如果JUnit 5扩展引起了您的兴趣,您可能还需要继续阅读文档的相应章节 。

翻译自: https://www.javacodegeeks.com/2016/04/replace-rules-junit-5.html

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

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

相关文章

微页面设计开发指南

一、目标实现左侧&#xff1a;为可用的组件列表&#xff0c;可拖动任一组件到中间的预览区域中间&#xff1a;为页面预览效果页面&#xff0c;选中任一组件&#xff0c;可在右侧进行参数配置右侧&#xff1a;为组件的参数配置&#xff08;选中中间的组件时出现&#xff09;&…

商城商品购买数量增减的完美JS效果

商城商品购买数量增减的完美JS效果 近期在开发一个地方O2O租书项目&#xff0c;使用ASP.NET MVC技术&#xff0c;其中在图书详情页&#xff0c;用户可以输入借阅的数量&#xff0c;这里使用了js来控制数量的增减和校验。 数量一定是数字 点击增减按钮的时候要能自动加1或减1 …

这款插件让你在VSCode上也能答题背单词

在VSCode上也可以在线答题了&#xff0c;插件市场上线了一款答题的插件&#xff0c;免去了去其它网站或者软件的烦恼&#xff0c;代码写累了&#xff0c;随手打开答题功能&#xff0c;换换脑子&#xff0c;或者熟悉两个单词&#xff0c;程序员的别样休闲时光&#xff0c;哈哈&a…

使用Java 8在地图上流式传输

在本文中&#xff0c;我将向您展示如何在标准Java映射上有效地实现Speedment Open Source流&#xff0c;并将Stream接口扩展为MapStream&#xff01; 即使在复杂的情况下&#xff0c;此添加将使保持流的具体性和可读性变得更加容易。 希望这将允许您继续流式传输而不会过早收集…

如何使用python给PDF文件加水印

Python作为编程界最火的语言&#xff0c;能做的事几乎你能想到的它都能干&#xff0c;就连抢茅台都可以&#xff0c;还有什么不行&#xff1f;&#xff01;Python作为脚本编程语言&#xff0c;可以做很多事情。使用Python&#xff0c;你可以轻松地给pdf加上水印。 你可以使用名…

搭建一个redis高可用系统

一、单个实例 当系统中只有一台redis运行时&#xff0c;一旦该redis挂了&#xff0c;会导致整个系统无法运行。 单个实例二、备份 由于单台redis出现单点故障&#xff0c;就会导致整个系统不可用&#xff0c;所以想到的办法自然就是备份&#xff08;一般工业界认为比较安全的备…

SSH连接远程服务器,本地known_hosts文件记录了什么

今天工作时&#xff0c;使用ssh命令远程连接公司的本地服务器时&#xff0c;突然出现以下错误bash-3.2$ ssh argus192.168.200.8 WARNING: REMOTE HOST IDENTIFICATION HAS CHANGED! IT IS POSSIBLE THAT SOMEONE IS DOING SOMETHING NASTY! Someone could be eavesdroppin…

“全人类的知识宝藏”维基百科迎来了20岁的生日!

维基百科从一个伟大的想法开始&#xff0c;与无数的像你像我一样的阅读者&#xff0c;创作者&#xff0c;捐赠者和粉丝经历了互联网的20年&#xff0c;今天让我们一起为这个属于所有互联网人的成果庆祝一次生日。值此20周年特地为它做了一个主页&#xff1a;https://wikimediaf…

转:智能音箱市场深度报告:怎么大家都在抢这个两亿小蛋糕?

原文链接&#xff1a;http://www.sohu.com/a/199335366_115978 智能音箱是今年最热的智能硬件项目之一。目前&#xff0c;智能音箱已经有了比较成熟的技术方案和模式思路&#xff0c;但消费市场似乎依然秉持着比较谨慎的态度。智能音箱市场上的主流产品都有什么思路&#xff1f…

Tailwindcss尤大神都fork了,是未来的趋势?

最近Tailwindcss频繁出现在我的视野里&#xff0c;从单词拼写中看&#xff0c;多多少少与css有点关系。近几年是JS框架大行其道&#xff0c;CSS方面少有新的框架出现。昨天突然看到尤大神在Github上的动态&#xff0c;fork了该项目&#xff0c;看来马上要火的节奏啊&#xff01…

JUnit 5 –架构

现在我们知道如何设置JUnit 5并使用它编写一些测试 &#xff0c;下面让我们看一下。 在本文中&#xff0c;我们将讨论JUnit 5架构以及采用这种方式的原因。 总览 这篇文章是有关JUnit 5的系列文章的一部分&#xff1a; 设定 基本 建筑 条件 注射 … JUnit 4 忽略Hamcre…

前端程序员书桌上不可缺少的CSS书籍

作为前端&#xff0c;CSS不仅要会&#xff0c;而且要精通&#xff0c;随着各种浏览器规范参差不齐和网页交互多元化的趋势越来越复杂&#xff0c;前端程序员必须要将CSS基础知识打牢。由于现在的框架越来越多&#xff0c;导致很大一部分程序员的工作只是拿着现成的组件布局&…

nodejs 进阶:图片缩小

demo 效果&#xff1a; 代码&#xff1a; /*** Created by ZXW on 2017/10/30.*/ var fs require(fs); var gm require(gm);gm(./不饿.jpg).resize(50, 50,"!").write(./不饿1.jpg, function (err) {if (!err) console.log(done);});2017-10-30 22:10:46转载于:ht…

可能是最先出来的关于介绍使用Vue3的一本书

Vue3 release版本已发布有几个月了&#xff0c;不少公司都已经开始使用vue3开发项目了&#xff0c;市场上的主流的框架如&#xff1a;Vant&#xff0c;Element UI&#xff0c;Taro也都发布了支持Vue3的版本。Vue3很多的开发优势自不必再说&#xff0c;学习上手vue3已经成为每个…

CSGL

glShadeModel void glShadeModel(GLenum mode) GL_FLAT/【GL_SMOOTH】 着色技术选择 glClearDepth GL.glClearDepth(depth); glClearDepth&#xff1a;设置深度缓存的清除值 参数 depth 指定清除深度缓存时使用的深度值。 说明 本函数指定用glClear清除深度缓存时所使用的深度值…

强大的Canvas开源库Fabric.js简介与开发指南

什么是Fabric.js&#xff1f;Fabric.js 是一个强大且简单的Javascript HTML5 Canvas库。官网地址&#xff1a;http://fabricjs.com/为什么要使用Fabric.js&#xff1f;Canvas提供一个好的画布能力, 但是Api不够友好。绘制简单图形其实还可以, 不过做一些复杂的图形绘制, 编写一…

模拟qq斗地主-准备发牌抢地主都是农民下一轮准备

为什么要搞这样一个项目&#xff1f;&#xff1f; 1&#xff0c;满足自己的java网络多线程编程的欲望&#xff01;因为之前一直都是搞web开发&#xff0c;服务器和客户端数据交流人家web服务器早就给你搞好了&#xff0c;比如tomcat,jetty...等等&#xff0c;其实之前脑子里就有…

rube3xxx_Rube GoldbergSpring整合

rube3xxxSpring Integration为集成系统所涉及的一些复杂性提供了非常好的抽象-Spring Integration从Integration的角度完美地满足了Facade的定义-简化了对复杂基础系统的访问。 为了说明这一点&#xff0c;请考虑一个简单的系统&#xff0c;该系统仅接收一条消息&#xff0c;然…

纯CSS实现React Logo图形,内含详细解析

以上是将要实现的效果&#xff0c;Javascript框架React的Logo图形&#xff0c;首先我们来拆解下&#xff0c;它包括三个交叉的椭圆和中间一个圆点&#xff0c;所以我们Html元素可以用以下代码实现&#xff1a;<div class"main"><div class"ellipse ell…

二次优化大招(由泰勒公式推出最值条件)

经过前两篇的铺垫&#xff0c;这一篇迎来了高潮。先说一句&#xff1a;特征值大法好&#xff01; 在开始正文之前&#xff0c;先看以下简单的推导 有了这些&#xff0c;理解下面的泰勒公式推出最值条件就容易了 转载于:https://www.cnblogs.com/Mr-ZeroW/p/7764916.html