原文地址: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. 分治法求解最近对问题(c++)

    #include"stdafx.h" #include<iostream> #include<cmath> #define TRUE 1 #define F ...

  2. JavaScript var关键字、变量的状态、异常处理、命名规范等介绍

    本篇主要介绍var关键字.变量的undefined和null状态.异常处理.命名规范. 目录 1. var 关键字:介绍var关键字的使用. 2. 变量的状态:介绍变量的未定义.已定义未赋值.已定义已 ...

  3. IE的F12开发人员工具不显示问题

    按下F12之后,开发人员工具在桌面上看不到,但是任务栏里有显示.将鼠标放在任务栏的开发人员工具上,出现一片透明的区域,选中之后却出不来.将鼠标移动到开发人员工具的缩略图上,右键-最大化,工具就全屏出现 ...

  4. 《Django By Example》第二章 中文 翻译 (个人学习,渣翻)

    书籍出处:https://www.packtpub.com/web-development/django-example 原作者:Antonio Melé (译者注:翻译完第一章后,发现翻译第二章的速 ...

  5. Linux 常用命令(持续补充)

    常用命令: command &:将进程放在后台执行 ctrl + z:暂停当前进程 并放入后台 jobs:查看当前后台任务 bg( %id):将任务转为后台执行 fg( %id):将任务调回前 ...

  6. MCDownloadManager ios文件下载管理器

    我们用AFNetworking小试牛刀,写一个简单的下载器来演示功能. 前言 为什么AFNetworking能够成为顶级框架?我们究竟该如何领悟它的精髓所在?这都是很难的问题.安全,高效,流畅,这3个 ...

  7. HTML5轻松实现搜索框提示文字点击消失---及placeholder颜色的设置

    在做搜索框的时候无意间发现html5的input里有个placeholder属性能轻松实现提示文字点击消失功能,之前还傻傻的在用js来实现类似功能... 示例 <form action=&quo ...

  8. SharePoint 2013管理中心里【管理服务器上的服务】不见了

    打开管理中心,准备配置Managed Metadata Service,发现"管理服务器上的服务"不见了 那我自己拼url直接访问:http://xxxx/_admin/Serve ...

  9. Tomcat 部署我的第一个程序

    idea 生成war包.先双击clean,再双击package.生成成功之后就会产生war包. 第二步:将生成好的war文件复制到tomcat文件夹下. 第三步:配置tomcat的server.xml ...

  10. angularjs 1 开发简单案例(包含common.js,service.js,controller.js,page)

    common.js var app = angular.module('app', ['ngFileUpload']) .factory('SV_Common', function ($http) { ...