扩展 junit 框架_JUnit 5 –扩展模型

扩展 junit 框架

我们已经对Java最普遍的测试框架的下一个版本了解很多。 现在,让我们看一下JUnit 5扩展模型,该模型将允许库和框架将自己的实现添加到JUnit中。

总览

  • 建立
  • 基本
  • 建筑
  • 扩展模型
  • 条件
  • 注射

在新兴的《 JUnit 5用户指南》中可以找到您将在此处阅读的更多内容以及更多内容。 请注意,它基于Alpha版本,因此可能会发生变化。

确实,我们鼓励我们提出问题或提出请求,以便JUnit 5可以进一步改进。 请利用这个机会! 这是我们帮助JUnit帮助我们的机会,因此,如果您能在这里看到一些改善,请确保将其上游 。

如有必要,此帖子将得到更新。 我在这里显示的代码示例可以在GitHub上找到 。

JUnit 4扩展模型

首先让我们看一下JUnit 4是如何解决该问题的。 它具有两个部分竞争的扩展机制:运行程序和规则。

跑步者

测试运行者管理测试的生命周期:实例化,调用设置和拆卸方法,运行测试,处理异常,发送通知等。JUnit4提供了实现所有这些功能的实现。

在4.0中,只有一种扩展JUnit的方法:创建一个新的运行器并使用@RunWith(MyRunner.class)注释测试类,以便JUnit使用它而不是其自己的实现。

该机制非常繁重,并且扩展范围很小。 而且它有一个非常严格的限制:每个测试班级只能有一个跑步者,这使得他们无法组成。 因此,无法同时利用Mockito和Spring跑步者的功能。

规则

为了克服这些限制,JUnit 4.7引入了rules ,它们是测试类的带注释字段。 JUnit 4将测试方法(和其他操作)包装到一条语句中,并将其传递给规则。 然后,他们可以在执行语句之前和之后执行一些代码。 此外,测试方法通常在执行期间在规则实例上调用方法。

一个示例是临时文件夹规则 :

public static class HasTempFolder {@Rulepublic TemporaryFolder folder= new TemporaryFolder();@Testpublic void testUsingTempFolder() throws IOException {File createdFile= folder.newFile("myfile.txt");File createdFolder= folder.newFolder("subfolder");// ...}
}

由于使用@Rule批注,JUnit调用带有包装方法testUsingTempFolder的语句的文件夹 。 编写此特定规则是为了使文件夹创建一个临时文件夹,执行测试,然后再删除该文件夹。 然后,测试本身可以在临时文件夹中创建文件和文件夹。

其他规则可能会在Swing的事件分发线程中运行测试 ,建立和拆除数据库,或者如果测试运行时间过长,则让测试超时 。

规则是一个很大的改进,但是通常仅限于在测试运行之前和之后执行一些代码。 他们无法帮助无法在该框架内实现的扩展。

事态

JUnit有两种相互竞争的扩展机制,每种都有其自身的局限性。

因此,自JUnit 4.7起,就有两种竞争的扩展机制,每种都有其自身的局限性,但也有很多重叠之处。 这使得干净扩展很困难。 此外,编写不同的扩展可能会出现问题,并且通常无法实现开发人员希望的扩展。

junit-5-extension-model

由Tony Walmsley在CC-BY 2.0下发布

JUnit 5扩展模型

JUnit Lambda项目具有两个核心原则 ,其中之一是“优先于功能而不是扩展点”。 从字面上看,这转化为新版本的整体机制–这不仅是扩展JUnit 5的唯一机制,也是最重要的机制。

延伸点

JUnit 5扩展可以声明对测试生命周期的某些特定时刻感兴趣。 当JUnit 5引擎处理测试时,它将逐步通过这些步骤并调用每个已注册的扩展。 从外观上看,这些是扩展点:

  • 测试实例后处理
  • 之前回调
  • 有条件的测试执行
  • 每次回调之前
  • 参数解析
  • 异常处理
  • AfterEach回调
  • 毕竟回调

(不要担心它们是否每个都不清楚。我们稍后会介绍其中的一些。)

每个扩展点对应一个接口。 他们的方法采用的参数可以捕获测试生命周期中特定点的上下文,例如测试实例和方法,测试名称,参数,注释等。

扩展可以实现任何数量的那些接口,并且将由引擎使用相应的参数进行调用。 然后,它可以执行实现其功能所需的任何操作。 需要考虑的一个细节:引擎在实例化扩展时以及将实例保留多长时间时不做任何保证,因此它们必须是无状态的。 他们需要维护的任何状态都必须写入JUnit并从中加载。

创建扩展后,剩下要做的就是告诉JUnit。 这是那么容易,因为添加@ExtendWith(MyExtension。 ),需要延长测试类或方法。

实际上,存在一个稍微不那么冗长和更多显示的选项。 但是为此,我们首先必须看看JUnit扩展模型的另一个Struts。

自定义注释

JUnit 5 API由注释驱动,当引擎检查它们的存在时,它会做一些额外的工作:它不仅在类,方法和参数上查找注释,还在其他注释上查找。 并且它将发现的所有内容都视为立即存在于所检查的元素上。 注释可以通过所谓的meta-annotations进行注释 ,很酷的是,所有JUnit注释都是完全meta的。

这样就可以轻松创建和编写在JUnit 5中完全可用的注释:

/*** We define a custom annotation that:* - stands in for '@Test' so that the method gets executed* - has the tag "integration" so we can filter by that,*   e.g. when running tests from the command line*/
@Target({ElementType.TYPE, ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
@Test
@Tag("integration")
public @interface IntegrationTest { }

然后我们可以像这样使用它:

@IntegrationTest
void runsWithCustomAnnotation() {// this gets executed// even though `@IntegrationTest` is not defined by JUnit
}

或者我们可以为扩展创建更简洁的注释:

@Target({ ElementType.TYPE, ElementType.METHOD, ElementType.ANNOTATION_TYPE })
@Retention(RetentionPolicy.RUNTIME)
@ExtendWith(ExternalDatabaseExtension.class)
public @interface Database { }

现在我们可以使用@Database代替@ExtendWith(ExternalDatabaseExtension。 )。 由于我们添加了ElementType ANNOTATION_TYPE到允许的目标列表中,它也是一个元注释,我们或其他人可以对其进行进一步的组合。

假设我们要对某些测试的运行时间进行基准测试。 首先,我们创建要使用的注释:

@Target({ ElementType.TYPE, ElementType.METHOD, ElementType.ANNOTATION_TYPE })
@Retention(RetentionPolicy.RUNTIME)
@ExtendWith(BenchmarkCondition.class)
public @interface Benchmark { }

它已经指向BenchmarkCondition ,我们将在接下来实现。 这是我们的计划:

  • 衡量整个测试类的运行时间,存储执行任何测试之前的时间
  • 衡量各个测试方法的运行时间,存储每次测试之前的时间
  • 执行测试方法后,检索测试的启动时间,计算并打印结果运行时
  • 执行完所有测试后,检索类的启动时间,计算并打印结果运行时
  • 仅当使用@Benchmark注释类或方法时,才执行任何此操作

最后一点可能不会立即显而易见。 为什么扩展名不会处理未用@Benchmark注释的方法? 这源于以下事实:如果将扩展应用于类,它将自动应用于其中的所有方法。 因此,如果我们的要求表明我们可能希望对类进行基准测试,但不一定要对所有单个方法进行基准测试,则需要排除它们。 我们通过检查它们是否被单独注释来做到这一点。

碰巧的是,前四个点直接对应于生命周期回调BeforeAllBeforeEachAfterEachAfterAll ,因此我们要做的就是实现四个对应的接口。 这些实现非常简单,它们只是按照我们上面所说的去做:

public class BenchmarkCondition implementsBeforeAllExtensionPoint, BeforeEachExtensionPoint,AfterEachExtensionPoint, AfterAllExtensionPoint {private static final Namespace NAMESPACE =Namespace.of("BenchmarkCondition");@Overridepublic void beforeAll(ContainerExtensionContext context) {if (!shouldBeBenchmarked(context))return;writeCurrentTime(context, LaunchTimeKey.CLASS);}@Overridepublic void beforeEach(TestExtensionContext context) {if (!shouldBeBenchmarked(context))return;writeCurrentTime(context, LaunchTimeKey.TEST);}@Overridepublic void afterEach(TestExtensionContext context) {if (!shouldBeBenchmarked(context))return;long launchTime = loadLaunchTime(context, LaunchTimeKey.TEST);long runtime = currentTimeMillis() - launchTime;print("Test", context.getDisplayName(), runtime);}@Overridepublic void afterAll(ContainerExtensionContext context) {if (!shouldBeBenchmarked(context))return;long launchTime = loadLaunchTime(context, LaunchTimeKey.CLASS);long runtime = currentTimeMillis() - launchTime;print("Test container", context.getDisplayName(), runtime);}private static boolean shouldBeBenchmarked(ExtensionContext context) {return context.getElement().isAnnotationPresent(Benchmark.class);}private static void writeCurrentTime(ExtensionContext context, LaunchTimeKey key) {context.getStore(NAMESPACE).put(key, currentTimeMillis());}private static long loadLaunchTime(ExtensionContext context, LaunchTimeKey key) {return (Long) context.getStore(NAMESPACE).remove(key);}private static void print(String unit, String displayName, long runtime) {System.out.printf("%s '%s' took %d ms.%n", unit, displayName, runtime);}private enum LaunchTimeKey {CLASS, TEST}
}

有趣的细节是shouldBeBenchmarked ,它使用JUnit的API毫不费力地确定当前元素是否被@Benchmark (元)注释,以及writeCurrentTime / loadLaunchTime ,后者使用存储来写入和读取启动时间。

  • 您可以在GitHub上找到代码 。

下一篇文章将讨论条件测试执行和参数注入,并显示有关如何使用相应扩展点的示例。 如果您迫不及待,请查看这篇文章 ,其中展示了如何将两个JUnit 4规则(条件禁用和临时文件夹)移植到JUnit 5。

摘要

我们已经看到,JUnit 4的运行者和规则对于创建干净,强大且可组合的扩展不是理想的选择。 JUnit 5旨在通过更通用的扩展点概念来克服它们的局限性。 它们允许扩展程序指定要在测试生命周期中的哪些时间点进行干预。 我们还研究了元注释如何使轻松创建自定义注释成为可能。

你怎么看?

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

扩展 junit 框架

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

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

相关文章

【斐波拉切数列第N项】

#include<iostream> using namespace std;int main() {int f[100];f[0] 0, f[1] 1;int n;cin >> n;for (int i 2; i < n; i){f[i] f[i - 1] f[i - 2];}cout <<f[n] << endl;return 0; }

python怎么下载安装mac_Mac下内置python2.7如何安装模块?

目前电脑里内置的版本是python2.7 用easy_install下载了几个模块&#xff0c;再输入pip list&#xff0c;得到&#xff1a; beautifulsoup4 (4.5.1) easygui (0.98.0) pip (8.1.2) setuptools (20.10.1) vboxapi (1.0) 可以确定我想要的bs4已经下载成功。然后我再输入python se…

jooq_SpringBoot:与JOOQ合作

jooq在上一篇文章SpringBoot&#xff1a;与MyBatis一起工作中&#xff0c;我们学习了如何使用SpringBoot MyBatis Starter快速启动并运行Spring和MyBatis。 在本文中&#xff0c;我们将学习如何使用SpringBoot JOOQ Starter。 JOOQ&#xff08;面向Java对象的查询&#xff09;…

【WebRTC---入门篇】(十二)WebRTC传输协议

浏览器协议栈(左图传统HTTP 右图WebRTC) RTP/SRTP RTP是未加密的数据,SRTP是加密后的数据。 RTP协议

android4.0支持m3u8格式,【报Bug】安卓下无法播放M3U8格式音频,报错

详细问题描述(DCloud产品不会有明显的bug&#xff0c;所以你遇到的问题大都是在特定环境下才能重现的问题&#xff0c;请仔细描述你的环境和重现方式&#xff0c;否则DCloud很难排查解决你的问题)[内容]安卓下小程序音频播放器播放&#xff2d;3&#xff35;8格式文件报错重现步…

springcloud官方文档_通俗易懂!Spring Cloud简介:官方文档翻译版

什么是微服务&#xff1f;"微服务架构是一种架构模式&#xff0c;它提倡将单一应用程序划分成一组小的服务&#xff0c;服务之间相互协调、互相配合&#xff0c;为用户提供最终价值。每个服务运行在其独立的进程中&#xff0c;服务和服务之间采用轻量级的通信机制相互沟通…

【RTMP协议分析与抓包实测】

传输协议 RTMP基本通讯 RTMP基于TCP之上传输 TCP三次握手,相关文章链接&#xff0c;TCP三次握手流程 进行握手 c- --> s 发送c0c1 c ---> s 发送c2 s ---> c 发送s0s1s2 建立RTMP连接 真实建立连接的场景 c- --> s RTMP发送connect建立连接 s ---> c 协商(滑动…

autowired_@Autowired所有的东西!

autowired最近&#xff0c;我写了Autowired注释 &#xff0c;它使我们可以编写更少的代码&#xff0c;从而使我们的生活更轻松 。 但是&#xff0c;使用它通常会使您的设计更加复杂。 尤其是当我们谈论在类的属性上使用它时。 它更容易违反 单一责任原则 。 这样可以更容易地注…

android message 代码,Android Handler移除Message详解及实例代码

Android Handler移除Message详解问题&#xff1a;1.removeMessage(what)函数是否只能移除对应what值的Message&#xff1f;2.对于Delayed发送的Message&#xff0c;能否提前remove&#xff1f;代码测试&#xff1a;package javine.k.testhandler;import android.app.Activity;i…

python中如何调用或修改元组中的元素_python 元组的使用方法

元组——tuple 列表非常适合用于存储在程序运行期间可能变化的数据集。 列表是可以修改的&#xff0c;但元组是不可修改的 Python将不能修改的值称为不可变的&#xff0c;而不可变的列表被称为元组 1. 元组的创建和删除 &#xff08;1&#xff09;使用赋值运算符直接创建元组 语…

【WebRTC---入门篇】(十三)WebRTC音视频数据采集

音视频采集API false表示不采集,true表示采集 WebRTC API适配 获取音视频设备的访问权限 通过 return navigator.mediaDevices.enumerateDevices();/*返回一个promise,为了获取音视频的权限*/ 视频约束

android按钮点击toast,关于button点击事件中setOnClick等元素的解读以及方法?以及toast的位置以及作用?...

此文末参考链接&#xff1a;此段代码的教程以及使用接口的方式、switch语句的教程链接为链接1.汇总里说的有更多的实现方法&#xff0c;为链接2。文中链接为视觉统一&#xff0c;链接均于文末&#xff0c;以上为方便文中跳转&#xff0c;加了文中的跳转链接。以下代码为我学习b…

java用什么编译器_Java用Java编译

java用什么编译器在上一篇文章中&#xff0c;我写了关于如何在运行时生成代理的内容&#xff0c;我们已经了解到生成Java源代码的程度。 但是&#xff0c;要使用该类&#xff0c;必须对其进行编译&#xff0c;并将生成的字节码加载到内存中。 那是“编译”时间。 幸运的是&…

app登录界面背景 css_计算机毕业设计中Java web实现简登录页面(MyBatis+jsp+servlet+html+css+javascript)...

点击上方“蓝字”&#xff0c;关注我们.本文利用MyBatisjspservlethtmlcssjavascript实现了一个简单的登录页面。对用户输入的用户名和密码就行校验&#xff0c;校验通过则登录成功&#xff0c;密码和用户信息保存在mysql表中&#xff0c;通过MyBatis访问(MyBatis相关知识可参考…

android strm,Android 关于so文件的随记

1.背景&#xff1a;项目中要集成商汤的活体检测sdk&#xff0c;2.遇到的问题&#xff1a;商汤提供的demo 可以正常运行&#xff0c;但是将sdk集成至项目中时一直报错&#xff0c;但是商汤侧却没办法提供具体的报错原因3.解决问题&#xff1a;反编译商汤的源码发现&#xff0c;报…

工业互联网二级节点建设_建设者还是二传手?

工业互联网二级节点建设不用说&#xff0c;每个对象都需要先创建才能使用。 无论我们是在谈论域&#xff0c;框架&#xff0c;库还是任何其他类型的类&#xff0c;都没有关系。 当您的代码是面向对象的时&#xff0c;这些类仅是对象的定义。 创建对象之前&#xff0c;不能使用它…

android 指针是什么意思,Android系统的智能指针(轻量级指针、强指针和弱指针)的实现原理分析(3)...

提供引用计数器的类RefBase我们就暂时介绍到这里&#xff0c;后面我们再结合智能指针类一起分析&#xff0c;现在先来看看强指针类和弱指针类的定义。强指针类的定义我们在前面介绍轻量级指针的时候已经见到了&#xff0c;就是sp类了&#xff0c;这里就不再把它的代码列出来了。…

【WebRTC---入门篇】(十五)WebRTC信令服务器实现

如何使用socket.io发送消息 io代表整个节点

气味识别应用_代码气味–第二部分

气味识别应用在上一篇文章《代码气味–第一部分》中 &#xff0c;我谈到了膨胀器&#xff1a;它们是代码气味&#xff0c;可以识别为长方法&#xff0c;大型类&#xff0c;原始痴迷&#xff0c;长参数列表和数据块。 在这一篇中&#xff0c;我想深入研究面向对象的滥用者和变更…