前言

org.junit.jupiter.api.extension包下,JUnit5提供了丰富的扩展接口,通过实现这些接口,我们可以定制自己的扩展并注册到JUnit中来实现功能扩展。

Extension接口

这个接口可以说是JUnit扩展中最为重要的接口,不过与它的重要性不相匹配的是,它没有定义任何的成员:

public interface Extension { }

然而它的子接口却极其丰富:

注册扩展的方式

前面说过,我们可以通过实现Extension的子接口来定制自己的扩展,那么注册又是如何做到的呢?在JUnit5中提供了两个注解来帮助我们将自定义扩展注册到JUnit,它们分别是:@ExtendWith@RegisterExtension

  • @ExtendWith

这个注解可标注在类型或方法上,通过声明式的方式注册扩展。是可重复注解的同时,又接受一个Extension类型的数组,当存在多个扩展时,你可在数组中传入多个Extension,抑或是结合@Extensions注解使用:

@Target({ ElementType.TYPE, ElementType.METHOD })
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Inherited
@Repeatable(Extensions.class)
public @interface ExtendWith {Class<? extends Extension>[] value();
}

  • @RegisterExtension

这个注解也是用来注册扩展的,但是它只能被用到字段上,且相对于@ExtendWith的声明式注册方式,它是编程式的注册方式。被它标注的字段可以是staticnon-static,但这个字段不能被private修饰,实际的值也不能为null

@Target(ElementType.FIELD)
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface RegisterExtension {}

另外,对于被static修饰和不被static修饰也要分情况讨论:

  • static:通过这种方式注册的扩展不受任何限制,你可以注册BeforeAllCallbackAfterAllCallbackTestInstanceFactoryTestInstancePostProcessorTestInstancePreDestroyCallbackBeforeEachCallback等等类型。
  • non-static:这种实例类型的注册往往会被延迟,它会在测试类实例化完成并且方法级别的@ExtendWith都注册之后,这回导致一些类级别或实例级别的扩展如:BeforeAllCallbackAfterAllCallbackTestInstanceFactoryTestInstancePostProcessor的注册出现问题。但是,如果你在测试类上标注了@TestInstance(Lifecycle.PER_CLASS),那么它将会被提前到方法级别的@ExtendWith注册之前注册。

@RegisterExtension标注的字段注册顺序是根据算法确定的,如果你想要明确他们的注册顺序,可以使用org.junit.jupiter.api.Order注解指定。

声明式 Vs 编程式

现在有必要考虑一个问题:声明式的注册意味着什么?为什么需要编程式的注册?

首先声明式注册的扩展都不支持手动调用扩展类的构造方法或工厂方法等传入参数,意味着它们的行为将无法定制,因而存在一定的局限性;由此才引出了编程式的注册方法,我们可以通过扩展的构造器或者工厂方法等传入参数,从而实现进一步的扩展定制!

这样一来我们便可知道,声明式有它的便利之处,而编程式也有它的强大之处,没有孰优孰劣,都有自己的适用场景。

注册示例

JUnit支持我们对异常做扩展,一个测试方法中可能会抛出异常,而这异常会被JUnit捕获。通过Extension下的子接口TestExecutionExceptionHandler,我们可以决定在捕获异常之后的行为。

由于@ExtendWith使用简单,且在其它文章中已多次用到,这里就不再赘述,只讲@RegisterExtension。先在测试类中创建一个ExceptionHandler

static class ExceptionHandler implements TestExecutionExceptionHandler {private String message;ExceptionHandler(String message) {this.message = message;}@Overridepublic void handleTestExecutionException(ExtensionContext context, Throwable throwable) throws Throwable {throw new RuntimeException(String.format("Display name: [%s], reason: [%s], raw message: [%s]",context.getDisplayName(), this.message, throwable.getMessage()));}}

之后通过字段方式注册它,同时写一个普通的测试方法,抛出一个异常:

class JunitSampleTests {static class ExceptionHandler implements TestExecutionExceptionHandler {// ...}@RegisterExtensionstatic ExceptionHandler exceptionHandler = new ExceptionHandler("异常被捕获了");@Testvoid test() {throw new RuntimeException("测试出错啦!");}
}

注意上面调用了ExceptionHandler的构造器传入了message,这是声明式的@ExtendWith无法做到的:

SPI注册

不知你是否了解过Java中的SPI(Service Provider Interface)机制,它可以用来启用框架扩展和替换组件,这在JUnit中是支持的,借此我们可以在不使用注解的情况下注册我们的扩展。

如果你是Maven工程,需要在test目录下的测试资源目录resources(这个目录一般不存在,需要手动创建并右键标记为“Test Resources Root”)中新建一个名为junit-platform.properties的文件(这个文件名不能随意,否则JUnit无法扫描到),并在其中加上配置:

junit.jupiter.extensions.autodetection.enabled=true

而后同样在这个resources下,新建目录WEB-INF/services,之后创建一个名为org.junit.jupiter.api.extension.Extension的文件,这个文件名其实就是Extension接口的全限定类名,而其中的内容就是我们实现的扩展类的全限定类名(如有多个扩展实现,一个限定名占一行):

com.tinysand.fileuploads.ExceptionHandler

这里我把之前的扩展实现单独提成了一个类文件,同时去掉了带参的构造器和成员,显然这种方式也只能像@ExtendWith注解做的那样。

最后,如果不想使用配置文件,你可以在VMRun/Debug Configurations中的VM options)参数添加如下参数而达到同样的效果:

-Djunit.jupiter.extensions.autodetection.enabled=true

软件版本

软件 版本
JUnit 5.6.2(junit-jupiter)

class ts 扩展方法_JUnit 5自定义扩展相关推荐

  1. 【Groovy】Groovy 扩展方法 ( 实例扩展方法配置 | 扩展方法示例 | 编译实例扩展类 | 打包实例扩展类字节码到 jar 包中 | 测试使用 Thread 实例扩展方法 )

    文章目录 一.扩展方法示例 二.实例扩展方法配置 三.编译实例扩展类 四.打包静态扩展类字节码到 jar 包中 五.测试使用 Thread 实例扩展方法 一.扩展方法示例 为 Thread 扩展 he ...

  2. 【Groovy】Groovy 扩展方法 ( 静态扩展方法配置 | 扩展方法示例 | 编译静态扩展类 | 打包静态扩展类字节码到 jar 包中 | 测试使用 Thread 静态扩展类 )

    文章目录 一.扩展方法示例 二.静态扩展方法配置 三.编译静态扩展类 四.打包静态扩展类字节码到 jar 包中 五.测试使用 Thread 静态扩展类 一.扩展方法示例 为 Thread 扩展 hel ...

  3. 扩展方法 枚举值_扩展枚举功能的两种方法

    扩展方法 枚举值 前言 在上一篇文章中,我解释了如何以及为什么在Java代码中使用enums而不是switch/case控制结构. 在这里,我将展示如何扩展现有enums功能. 介绍 Java enu ...

  4. el_table expand扩展单元格,自定义扩展样式

    当我们接到table扩展某行的需求(如下),当然第一反应是去参考element官方网站,官网提供给我们很多的模板样例. 但是样例也仅展示一个">"的标识,当需要支持:比如点击 ...

  5. ORACLE 表空间扩展方法,ORACLE 表空间扩展方法

    第一步:查看表空间的名字及文件所在位置: select tablespace_name, file_id, file_name, round(bytes/(1024*1024),0) total_sp ...

  6. java对象扩展方法_高可扩展的面向对象代码架构是如何设计的

    导语 Java后端程序员的日常工作,大多数可能都是写基于数据库CRUD的Dao层.Manager层.Service层.Controller层,需求来了,就对着这几个层一顿怼代码.调试跑通了,就完事了. ...

  7. Unity自定义扩展方法

    问题背景 在使用unity开发过程中,通常会遇到一种情况,比如说给物体重新赋值坐标的问题, Transfrom tran:float pos_x=1,pos_y=1,pos_z=1;tran.posi ...

  8. C#学习笔记四: C#3.0自动属性匿名属性及扩展方法

    前言 这一章算是看这本书最大的收获了, Lambda表达式让人用着屡试不爽, C#3.0可谓颠覆了我们的代码编写风格. 因为Lambda所需篇幅挺大, 所以先总结C#3.0智能编译器给我们带来的诸多好 ...

  9. jquery的扩展方法介绍

    最近一直在写js,这其中也少不了一位js的主角了jQuery,下面介绍的是jQuery的一些扩展,也就是jQuery的扩展方法,jQuery的扩展方法有两种方式,一种是jQuery本身的扩展方法,另一 ...

最新文章

  1. 离散数学中的联结符号
  2. C++实现大数的乘法
  3. 《零基础入门学习Python》学习过程笔记【30模块中的函数,os模块,ospath模块中的函数(看了一点)】...
  4. 向量收敛在matlab中,matlab实验报告
  5. 【模板】可持久化并查集
  6. 初一计算机下册理论知识,初中信息技术七年级下册(第七版)
  7. Xorg可以使用hot-plug了,不过配置很麻烦
  8. Linux shell 的条件 / 比较语法
  9. 什么是document对象?如何获取文档对象上的元素?_JavaScript DOM操作元素的方法,你还记得多少?...
  10. python计算器函数图像_Python图形计算器,python,图像,化
  11. 基于SSM的房屋租赁管理系统
  12. 读书笔记之怎样在股市获得稳健收益
  13. python拆分word文档_python-docx处理word文档
  14. 使用table自带的deleteRow,insertRow方法实现表格内容滚动
  15. Haar特征和级联分类器目标检测介绍及应用
  16. 表格css样式 ——表格背景,隔行变色,触摸表格变色
  17. [gazebo_gui-2] process has died [pid 4588, exit code 134, cmd /opt/ros/kinetic/lib/gazebo_ros/gzc
  18. Java简单的GUI考试系统
  19. 如何使用 Python 开发一个【抖音视频下载神器】
  20. 相量法matlab仿真编程,电力系统的MATLAB/SIMULINK仿真与应用 王晶,翁国庆,张有兵著 西安电子科技大学出版社 9787560620...

热门文章

  1. c语言个人账册报告的课题来源,C语言个人账簿管理系统报告
  2. 打包文档_苏教版小学数学16年级全十二册教案Word文档打包下载
  3. cdt规约报文用程序解析_用Python运维网络(5):scapy
  4. web页面刷不出来 白色_今日头条连接超时刷不出来解决方案
  5. struts2文件上传(2)
  6. git图文工具_全网最详细的Windows里Git client客户端管理工具SourceTree的下载与安装(图文详解)...
  7. php怎么关联默认打开程序,win10系统打开文件时提示“请在默认程序控制面板中创建关联”如何解决...
  8. python实现pdf转excel_python pdf转Excel
  9. android sharedpre,Android SharedPreferences四种操作模式使用详解_Android_脚本之家
  10. 虚拟机当作设置服务器,虚拟机当作设置服务器