「译」JUnit 5 系列:条件测试
原文地址:http://blog.codefx.org/libraries/junit-5-conditions/
原文日期:08, May, 2016
译文首发:Linesh 的博客:「译」JUnit 5 系列:条件测试
我的 Github:http://github.com/linesh-simplicity
上一节我们了解了 JUnit 新的扩展模型,了解了它是如何支持我们向引擎定制一些行为的。然后我还预告会为大家讲解条件测试,这一节主题就是它了。
条件测试,指的是允许我们自定义灵活的标准,来决定一个测试是否应该执行。条件(condition) 官方的叫法是条件测试执行。
概述
(如果不喜欢看文章,你可以戳这里看我的演讲,或者看一下最近的 vJUG 讲座,或者我在 DevoxxPL 上的 PPT。
本系列文章都基于 Junit 5发布的先行版 Milestone 2。它可能会有变化。如果有新的里程碑(milestone)版本发布,或者试用版正式发行时,我会再来更新这篇文章。
这里要介绍的多数知识你都可以在 JUnit 5 用户指南 中找到(这个链接指向的是先行版 Milestone 2,想看的最新版本文档的话请戳这里),并且指南还有更多的内容等待你发掘。下面的所有代码都可以在 我的 Github 上找到。
目录
- 相关的扩展点
- 动手实现一个@Disabled 注解
- @DisabledOnOs
- 一种简单的实现方式
- 更简洁的API
- 代码重构
- @DisabledIfTestFails
- 异常收集
- 禁用测试
- 集成
- 回顾总结
- 分享&关注
相关的扩展点
还记得 拓展点 一节讲的内容吗?不记得了?好吧,简单来说,JUnit 5 中定义了许多扩展点,每个扩展点都对应一个接口。你自己的扩展可以实现其中的某些接口,然后通过 @ExtendWith
注解注册给 JUnit,后者会在特定的时间点调用你的接口实现。
要实现条件测试,你需要关注其中的两个扩展点: ContainerExecutionCondition
(容器执行条件)和 TestExecutionCondition
(测试执行条件)。
public interface ContainerExecutionCondition extends Extension {
/**
* Evaluate this condition for the supplied ContainerExtensionContext.
*
* An enabled result indicates that the container should be executed;
* whereas, a disabled result indicates that the container should not
* be executed.
*
* @param context the current ContainerExtensionContext; never null
* @return the result of evaluating this condition; never null
*/
ConditionEvaluationResult evaluate(ContainerExtensionContext context);
}
public interface TestExecutionCondition extends Extension {
/**
* Evaluate this condition for the supplied TestExtensionContext.
*
* An enabled result indicates that the test should be executed;
* whereas, a disabled result indicates that the test should not
* be executed.
*
* @param context the current TestExtensionContext; never null
* @return the result of evaluating this condition; never null
*/
ConditionEvaluationResult evaluate(TestExtensionContext context);
}
ContainerExecutionCondition
接口将决定容器中的测试是否会被执行。通常情况下,你使用 @Test
注解来标记测试,此时测试所在的类就是容器。同时,单独的测试方法是否执行则是由 TestExecutionCondition
接口决定的。
(这里,我说的是“通常情况下”,因为其他测试引擎可能对容器和测试有截然不同的定义。但一般情况下,测试就是单个的方法,容器指的就是测试类。)
嗯,基本知识就这么多。想实现条件测试,至少需要实现以上两个接口中的一个,并在接口的 evalute
方法中执行自己的条件检查。
动手实现一个@Disabled 注解
最简单的“条件”就是判断都没有,直接禁用测试。如果在方法上发现了 @Disabled
注解,我们就直接禁用该测试。
让我们来写一个这样的 @Disabled
注解吧:
@Target({ ElementType.TYPE, ElementType.METHOD })
@Retention(RetentionPolicy.RUNTIME)
@ExtendWith(@DisabledCondition.class)
public @interface Disabled { }
对应的扩展如下:
public class DisabledCondition
implements ContainerExecutionCondition, TestExecutionCondition {
private static final ConditionEvaluationResult ENABLED =
ConditionEvaluationResult.enabled("@Disabled is not present");
@Override
public ConditionEvaluationResult evaluate(
ContainerExtensionContext context) {
return evaluateIfAnnotated(context.getElement());
}
@Override
public ConditionEvaluationResult evaluate(
TestExtensionContext context) {
return evaluateIfAnnotated(context.getElement());
}
private ConditionEvaluationResult evaluateIfAnnotated(
Optional<AnnotatedElement> element) {
Optional<Disabled> disabled = AnnotationUtils
.findAnnotation(element, Disabled.class);
if (disabled.isPresent())
return ConditionEvaluationResult
.disabled(element + " is @Disabled");
return ENABLED;
}
}
写起来小菜一碟吧?在 JUnit 真实的产品代码中,@Disabled
也是这么实现的。不过,有两个地方有一些细微的差别:
- 官方
@Disabled
注解不需要再使用@ExtendWith
注册扩展,因为它是默认注册了的 - 官方
@Disabled
注解可以接收一个参数,解释测试被忽略的理由。它会在测试被忽略时被记录下来
使用时请注意,AnnotationUtils
是个内部 API。不过,官方可能很快就会将它提供的功能给开放出来。
接下来让我们写点更有意思的东西吧。
@DisabledOnOs
如果有些测试我们只想让它在特定的操作系统上面运行,这个要怎么实现呢?
一种简单的实现方式
当然,我们还是从注解开始咯:
@Target({ ElementType.TYPE, ElementType.METHOD })
@Retention(RetentionPolicy.RUNTIME)
@ExtendWith(OsCondition.class)
public @interface DisabledOnOs {
OS[] value() default {};
}
这回注解需要接收一个或多个参数值,你需要告诉它想禁用测试的操作系统有哪些。 OS
是个枚举类,定义了所有操作系统的名字。同时,它还提供了一个静态的 static OS determine()
方法,你可能已经从名字猜到了,它会推断并返回你当前所用的操作系统。
现在我们可以着手实现 OsCondition
扩展类了。它必须检查两点:注解是否存在,以及当前操作系统是否在注解声明的禁用列表中。
public class OsCondition
implements ContainerExecutionCondition, TestExecutionCondition {
// both `evaluate` methods forward to `evaluateIfAnnotated` as above
private ConditionEvaluationResult evaluateIfAnnotated(
Optional<AnnotatedElement> element) {
Optional<DisabledOnOs> disabled = AnnotationUtils
.findAnnotation(element, DisabledOnOs.class);
if (disabled.isPresent())
return disabledIfOn(disabled.get().value());
return ENABLED;
}
private ConditionEvaluationResult disabledIfOn(OS[] disabledOnOs) {
OS os = OS.determine();
if (Arrays.asList(disabledOnOs).contains(os))
return ConditionEvaluationResult
.disabled("Test is disabled on " + os + ".");
else
return ConditionEvaluationResult
.enabled("Test is not disabled on " + os + ".");
}
}
然后使用的时候就可以像这样:
@Test
@DisabledOnOs(OS.WINDOWS)
void doesNotRunOnWindows() {
assertTrue(false);
}
棒。
更简洁的API
但代码还可以写得更好!JUnit 的注解是可组合的,基于此我们可以让这个条件注解更简洁:
@TestExceptOnOs(OS.WINDOWS)
void doesNotRunOnWindowsEither() {
assertTrue(false);
}
@TestExceptionOnOs
完美的实现方案是这样的:
@Retention(RetentionPolicy.RUNTIME)
@Test
@DisabledOnOs(/* 通过某种方式取得注解下的 `value` 值 */)
public @interface TestExceptOnOs {
OS[] value() default {};
}
测试实际运行时, OsCondition::evaluateIfAnnotated
方法会扫描 @DisabledOnOs
注解,然后我们发现它又是对 @TestExceptOnOs
的注解,前面写的代码就可以如期工作了。但我不知道如何在 @DisabledOnOs
注解中获取 @TestExceptOnOs
中的value()
值。
「译」JUnit 5 系列:条件测试的更多相关文章
- 「译」JUnit 5 系列:扩展模型(Extension Model)
原文地址:http://blog.codefx.org/design/architecture/junit-5-extension-model/ 原文日期:11, Apr, 2016 译文首发:Lin ...
- 「译」JUnit 5 系列:环境搭建
原文地址:http://blog.codefx.org/libraries/junit-5-setup/ 原文日期:15, Feb, 2016 译文首发:Linesh 的博客:环境搭建 我的 Gith ...
- 「译」JUnit 5 系列:架构体系
原文地址:http://blog.codefx.org/design/architecture/junit-5-architecture/ 原文日期:29, Mar, 2016 译文首发:Linesh ...
- 「译」JUnit 5 系列:基础入门
原文地址:http://blog.codefx.org/libraries/junit-5-basics/ 原文日期:25, Feb, 2016 译文首发:Linesh 的博客:JUnit 5 系列: ...
- jvm系列(十):如何优化Java GC「译」
本文由CrowHawk翻译,是Java GC调优的经典佳作. 本文翻译自Sangmin Lee发表在Cubrid上的"Become a Java GC Expert"系列文章的第三 ...
- jvm系列(七):如何优化Java GC「译」
本文由CrowHawk翻译,地址:如何优化Java GC「译」,是Java GC调优的经典佳作. Sangmin Lee发表在Cubrid上的”Become a Java GC Expert”系列文章 ...
- 「译」JavaScript 的怪癖 1:隐式类型转换
原文:JavaScript quirk 1: implicit conversion of values 译文:「译」JavaScript 的怪癖 1:隐式类型转换 译者:justjavac 零:提要 ...
- iOS 9,为前端世界都带来了些什么?「译」 - 高棋的博客
2015 年 9 月,Apple 重磅发布了全新的 iPhone 6s/6s Plus.iPad Pro 与全新的操作系统 watchOS 2 与 tvOS 9(是的,这货居然是第 9 版),加上已经 ...
- 「译」forEach循环中你不知道的3件事
前言 本文925字,阅读大约需要7分钟. 总括: forEach循环中你不知道的3件事. 原文地址:3 things you didn't know about the forEach loop in ...
随机推荐
- 【AR实验室】mulberryAR : ORBSLAM2+VVSION
本文转载请注明出处 —— polobymulberry-博客园 0x00 - 前言 mulberryAR是我业余时间弄的一个AR引擎,目前主要支持单目视觉SLAM+3D渲染,并且支持iOS端,但是该引 ...
- NodeJs之调试
关于调试 当我们只专注于前端的时候,我们习惯性F12,这会给我们带来安全与舒心的感觉. 但是当我们使用NodeJs来开发后台的时候,我想噩梦来了. 但是也别泰国担心,NodeJs的调试是很不方便!这是 ...
- 闲来无聊,研究一下Web服务器 的源程序
web服务器是如何工作的 1989年的夏天,蒂姆.博纳斯-李开发了世界上第一个web服务器和web客户机.这个浏览器程序是一个简单的电话号码查询软件.最初的web服务器程序就是一个利用浏览器和web服 ...
- JavaScript 自定义对象
在Js中,除了Array.Date.Number等内置对象外,开发者可以通过Js代码创建自己的对象. 目录 1. 对象特性:描述对象的特性 2. 创建对象方式:对象直接量.new 构造函数.Objec ...
- Ubuntu 16.10 安装byzanz截取动态效果图工具
1.了解byzanz截取动态效果图工具 byzanz能制作文件小,清晰的GIF动态效果图,不足就是,目前只能通过输入命令方式来录制. byzanz主要的参数选项有: -d, --duration=SE ...
- 窥探Vue.js 2.0 - Virtual DOM到底是个什么鬼?
引言 你可能听说在Vue.js 2.0已经发布,并且在其中新添加如了一些新功能.其中一个功能就是"Virtual DOM". Virtual DOM是什么 在之前,React和Em ...
- ASP.NET MVC开发日常一:SessionID合理清除
在MVC Web开发中临时存储数据一般会用到Session,Cookie,ViewBag,ViewData,TempData.每个的使用场景是不同,具体区别有空再补上. Session数据最敏感,最需 ...
- VS2015在创建项目时的一些注意事项
一.下面是在创建一个新的项目是我最常用的,现在对他们一一做一个详细的介绍: 1.Win32控制台应用程序我平时编写小的C/C++程序都用它,它应该是用的最多的. 2.名称和解决方案名称的区别:名称是项 ...
- BPM配置故事之案例12-触发另外流程
还记得阿海么,对就是之前的那个采购员,他又有了些意见. 阿海:小明,你看现在的流程让大家都这么方便,能不能帮个忙让我也轻松点啊-- 小明:--你有什么麻烦,现在不是已经各个部门自己提交申请了嘛? 阿海 ...
- git和pycharm管理代码
首先明白三个概念,服务器代码库,本地代码库,和正在coding的项目. coding完毕后,先通过commit提交到本地代码库,然后通过push再提交server的代码库 git步骤 git c ...