原文地址: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 系列:条件测试的更多相关文章

  1. 「译」JUnit 5 系列:扩展模型(Extension Model)

    原文地址:http://blog.codefx.org/design/architecture/junit-5-extension-model/ 原文日期:11, Apr, 2016 译文首发:Lin ...

  2. 「译」JUnit 5 系列:环境搭建

    原文地址:http://blog.codefx.org/libraries/junit-5-setup/ 原文日期:15, Feb, 2016 译文首发:Linesh 的博客:环境搭建 我的 Gith ...

  3. 「译」JUnit 5 系列:架构体系

    原文地址:http://blog.codefx.org/design/architecture/junit-5-architecture/ 原文日期:29, Mar, 2016 译文首发:Linesh ...

  4. 「译」JUnit 5 系列:基础入门

    原文地址:http://blog.codefx.org/libraries/junit-5-basics/ 原文日期:25, Feb, 2016 译文首发:Linesh 的博客:JUnit 5 系列: ...

  5. jvm系列(十):如何优化Java GC「译」

    本文由CrowHawk翻译,是Java GC调优的经典佳作. 本文翻译自Sangmin Lee发表在Cubrid上的"Become a Java GC Expert"系列文章的第三 ...

  6. jvm系列(七):如何优化Java GC「译」

    本文由CrowHawk翻译,地址:如何优化Java GC「译」,是Java GC调优的经典佳作. Sangmin Lee发表在Cubrid上的”Become a Java GC Expert”系列文章 ...

  7. 「译」JavaScript 的怪癖 1:隐式类型转换

    原文:JavaScript quirk 1: implicit conversion of values 译文:「译」JavaScript 的怪癖 1:隐式类型转换 译者:justjavac 零:提要 ...

  8. iOS 9,为前端世界都带来了些什么?「译」 - 高棋的博客

    2015 年 9 月,Apple 重磅发布了全新的 iPhone 6s/6s Plus.iPad Pro 与全新的操作系统 watchOS 2 与 tvOS 9(是的,这货居然是第 9 版),加上已经 ...

  9. 「译」forEach循环中你不知道的3件事

    前言 本文925字,阅读大约需要7分钟. 总括: forEach循环中你不知道的3件事. 原文地址:3 things you didn't know about the forEach loop in ...

随机推荐

  1. Tomcat一个BUG造成CLOSE_WAIT

    之前应该提过,我们线上架构整体重新架设了,应用层面使用的是Spring Boot,前段日子因为一些第三方的原因,略有些匆忙的提前开始线上的内测了.然后运维发现了个问题,服务器的HTTPS端口有大量的C ...

  2. wordpress多站点配置

    wordpress作为全球第一的个人博客搭建平台一直在国内外有着较高的人气,从3.0版本开始就已经支持多站点的搭建.该功能可以让子站点运行主站点的程序,不需要再每个站点分别存放网站程序.最近更新的4. ...

  3. 移动先行之谁主沉浮? 带着你的Net飞奔吧!

    移动系源码:https://github.com/dunitian/Windows10 移动系文档:https://github.com/dunitian/LoTDotNet/tree/master/ ...

  4. 【.net 深呼吸】限制执行代码的权限

    前面好几篇文章,老周都跟大伙伴们聊了跟应用程序域有关的话题,干脆咱们一聊到底吧,做学问就应该这样,有恒心. App Domain的创建新应用程序域的方法中,有一个特殊的重载: public stati ...

  5. javascript工厂模式和构造函数模式创建对象

    一.工厂模式 工厂模式是软件工程领域一种广为人知的设计模式,这种模式抽象了创建具体对象的过程(本书后面还将讨论其他设计模式及其在JavaScript 中的实现).考虑到在ECMAScript 中无法创 ...

  6. 【NLP】干货!Python NLTK结合stanford NLP工具包进行文本处理

    干货!详述Python NLTK下如何使用stanford NLP工具包 作者:白宁超 2016年11月6日19:28:43 摘要:NLTK是由宾夕法尼亚大学计算机和信息科学使用python语言实现的 ...

  7. 利用注册表在右键添加VS15的快捷方式打开文件夹

    1.简介 最近安装VS15 Preview 5,本版本可以打开"文件夹" 是否可以向Visual Studio Code一样在文件夹或文件右键菜单添加"Open with ...

  8. Java实现FTP文件与文件夹的上传和下载

    Java实现FTP文件与文件夹的上传和下载 FTP 是File Transfer Protocol(文件传输协议)的英文简称,而中文简称为"文传协议".用于Internet上的控制 ...

  9. windows charles response 乱码解决办法

    使用windows 版本的charles来做代理,发现服务端返回的response会出现中文乱码的情况, 查看软件设置,遗憾的是并没有关于编码的选项. 好在charles windows版本安装目录下 ...

  10. JBPM

    JBPM简介 什么是jbpm JBPM,全称是Java Business Process Management(业务流程管理),它是覆盖了业务流程管理.工作流.服务协作等领域的一个开源的.灵活的.易扩 ...