Feature Toggle,简单来说,就是一个开关,将未完成功能的代码屏蔽后发布到生产环境,从而避免多分支的情况。之所以有本文的产生,就是源于此情景。在引入Feature Toggle的同时,我们发现之前对这些未开发完功能的代码的单元测试不是很稳定,而且如果我们在用feature toggle关掉这个功能之后,这些测试也是对发布毫无价值可言,所有我们需要将这些测试全部屏蔽掉,以免影响运行其他测试结果。

在经过项目组讨论之后,我们毅然决然摒弃了直接采用@Ignore的低级做法,决定自己来实现一个简单的toggle,用annotation加读取配置文件的方式管理需要被屏蔽的测试。下面先介绍两种方式来实现这一toggle:

这两种方法都必须定义一个annotation和配置文件,定义如下:

@Target({ElementType.TYPE, ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
public @interface FeatureToggle {
public String feature();
}

1.先介绍一种简单的实现,也就是给测试实现一个runner, 封装一层判断条件,让他处理掉包含我们自己定义的annotation从而达到toggle的开关作用。

public class FeatureRunner extends BlockJUnit4ClassRunner {
@Override
protected void runChild(FrameworkMethod method, RunNotifier notifier) {
FeatureToggle annotationOnMethod = method.getAnnotation(FeatureToggle.class);
FeatureToggle annotationOnClass = method.getMethod().getDeclaringClass().getAnnotation(FeatureToggle.class); if (annotationOnClass != null) {
annotationOnMethod = annotationOnClass;
} String featureToggle = getToggleValue(annotationOnMethod); //从配置文件中读取annotationOnMethod.feature() 在properties文件中对应的value if (annotationOnMethod != null && "off".equals(featureToggle)) {
notifier.fireTestIgnored(describeChild(method));
} else {
super.runChild(method, notifier);
}
}
}

接下来在测试代码中使用方法如下:

@RunWith(FeatureRunner.class)
public class FeatureRunnerTest { @Test
@FeatureToggle(feature = "feature")
public void testTurnOn() throws Exception {
fail();
}
}

配置文件只需设置 feature=off 即可关掉所有同一个功能的所有加有annotation的测试。

 

2.采用@Rule的方式。JUnit 4.7 已经发布了,该版本具有一个重要的新特性:Rule。来一段最新junit框架里BlockJUnit4ClassRunner类中的代码来

    protected Statement methodBlock(FrameworkMethod method) {
Object test;
try {
test= new ReflectiveCallable() {
@Override
protected Object runReflectiveCall() throws Throwable {
return createTest();
}
}.run();
} catch (Throwable e) {
return new Fail(e);
}
Statement statement= methodInvoker(method, test);
statement= possiblyExpectingExceptions(method, test, statement);
statement= withPotentialTimeout(method, test, statement);
statement= withBefores(method, test, statement);
statement= withAfters(method, test, statement);
statement= withRules(method, test, statement);
return statement;
}

这段代码实际上可以看出在执行@Before和@After操作的同时,还会调用@Rule的,对于具体的实现大家可以去这个类里面深究,我就直接告诉大家,@Rule会在before之前执行。

下面是如何自己写一个类来实现TestRule从来控制测试是否执行:

public class FeatureToggleRule implements TestRule {
@Override
public Statement apply(Statement base, Description description) {
FeatureToggle annotationOnClass = description.getTestClass().getAnnotation(FeatureToggle.class);
FeatureToggle annotationOnMethod = description.getAnnotation(FeatureToggle.class);
if (annotationOnClass != null) {
annotationOnMethod = annotationOnClass;
} if (annotationOnMethod != null && isToggleOFF(annotationOnMethod)) { //读取配置文件
return new IgnoreStatement();
}
return base;
} class IgnoreStatement extends Statement {
@Override
public void evaluate() throws Throwable {
throw new AssumptionViolatedException("ignore by rule");
}
}
}

这里如果方法名或者函数名上又跟第一种实现同样的annotation和配置文件,加上如下声明:

public class FeatureToggleRuleTest {
@Rule
public TestRule rule = new FeatureToggleRule(); @Test
@FeatureToggle(feature = "feature")
public void testTurnOn() throws Exception {
fail();
}
}

Rule更具有灵活性,并且功能不仅仅如此,我们可以在给自己的测试添加很多rule规则,这里不进行深究。

总结:两种方式,都实现同一个思想,用注解加配置文件来控制所有未完成功能测试部分的开关控制,而且简单易行,并且可以为大家拓展更多的junit需求提供指导参考,谢谢。

Feature Toggle JUnit的更多相关文章

  1. 基于Feature Flag的下一代开发模式

    渐进式发布(Progressive Delivery)被认为是持续发布(Continous Delivery)的下一代形态,其专注于增强发布过程控制与降低发布风险,最终提高整体收益.国际科技巨头比如A ...

  2. spotify engineering culture part 1

    原文 ,因为原视频说的太快太长, 又没有字幕,于是借助youtube,把原文听&打出来了. 中文版日后有时间再翻译. one of the big succeess factors here ...

  3. go语言项目汇总

    Horst Rutter edited this page 7 days ago · 529 revisions Indexes and search engines These sites prov ...

  4. 近期关于CI/CD策略以及git分支模型的思考

    近两个月由于个人处于新环境.新项目的适应阶段,没怎么提笔写些文章.中间有好几个想法想记录下来分享,但受限于没有很好的时间段供自己总结思考(也可以总结为间歇性懒癌和剧癌发作),便啥也没有更新.借这个周末 ...

  5. Golang优秀开源项目汇总, 10大流行Go语言开源项目, golang 开源项目全集(golang/go/wiki/Projects), GitHub上优秀的Go开源项目

    Golang优秀开源项目汇总(持续更新...)我把这个汇总放在github上了, 后面更新也会在github上更新. https://github.com/hackstoic/golang-open- ...

  6. 版本分支管理标准 - Trunk Based Development 主干开发模型

    之前分享过<版本分支管理标准 - Git Flow>,不过在实际使用过程中, 因为其有一定的复杂度,使用起来较为繁琐,所以一些人员较少的团队并不会使用这个方案. 在这基础上,一些新的分支管 ...

  7. 解决fastjson反序列化日期0000-00-00失败的方案

    解决fastjson反序列化日期0000-00-00失败的方案 22 Jul 2016 一.案例场景复原 示例场景里涉及两个class:TestDemo.java, DateBeanDemo.java ...

  8. Cucumber(3)——命令以及日志

    目录 回顾 基本执行命令 关于日志的生成 回顾 在上一节中,我介绍了cucumber一些基本的语法内容,如果你还没有进行相关的了解或者环境的配置,你可以点击这里来进行了解一下 在本节中,我会对cucu ...

  9. git原理及如何选择分支模式

    一.git 原理介绍 1.git的四个工作区域 Git有四个工作区域:工作目录(Working Directory).暂存区(Stage/Index).资源库(Repository或Git Direc ...

随机推荐

  1. 关于Cocos2d-x发布游戏的时候遇到的问题和解决

    发布经常会遇到各种各样的问题,发布失败会返回一些值,但是这些值并不是重点,要看发布过程中产生的日志才能真正找到问题所在.我在发布自己做的第一个游戏的时候,遇到了各种各样的问题,不过都一一解决,下面是问 ...

  2. 关于Cocos2d-x项目运行的过程和场景切换步骤

    1.AppDelegate.cpp文件里面的applicationDidFinishLaunching函数其实可以看做C语言里面的main函数,是整个游戏运行的入口 这个函数里面的语句 auto sc ...

  3. linux -- ubuntu 脚本开机自启动

    有一个脚本想在ubuntu 虚拟机开机时,自动运行. 创建脚本文件 在“/opt/lampp/”下新建一个脚本文件:lampp_start.sh #!/bin/bash /opt/lampp/lamp ...

  4. MJRefresh原理分析

    MJRefresh是流行的下拉刷新控件.前段时间为了修复一个BUG.读了它的源代码.本文总结一下实现的原理 下拉刷新的基本原理 大部分的下拉刷新控件.都是用contentInset实现的.默认情况下. ...

  5. 【Java面试题】50 垃圾回收器的基本原理是什么?垃圾回收器可以马上回收内存吗?有什么办法主动通知虚拟机进行垃圾回收?

    1.对于GC来说,当程序员创建对象时,GC就开始监控这个对象的地址.大小以及使用情况. 通常,GC采用有向图的方式记录和管理堆(heap)中的所有对象.通过这种方式确定哪些对象是"可达的&q ...

  6. 【Java集合的详细研究5】Java中Array与ArrayList的主要区别

    1)精辟阐述:可以将 ArrayList想象成一种“会自动扩增容量的Array”. 2)Array([]):最高效:但是其容量固定且无法动态改变:     ArrayList:  容量可动态增长:但牺 ...

  7. office2003 下载地址 及密码

    http://www.downxia.com/downinfo/63.html microsoft office 2003 密钥 GWH28-DGCMP-P6RC4-6J4MT-3HFDY Micro ...

  8. Unity对象查找

    1. GameObject.Find  全局摄像机 全局画布 全局灯光 无法查找隐藏对象 ,效率低下,要用完全的路径来提升查找效率 2. transform.Find  UI中全部使用此方法 可以查找 ...

  9. JavaScript编码encode和decode escape和unescape

    encodeURI() 函数可把字符串作为 URI 进行编码. 语法 encodeURI(URIstring) 参数 描述 URIstring 必需.一个字符串,含有 URI 或其他要编码的文本. 返 ...

  10. IE6图片元素img下出现多余空白问题

    在进行页面的 DIV+CSS排版时,遇到IE6(当然有时Firefox下也会偶遇)浏览器中的图片元素img下出现多余空白的问题绝对是常见的对于该问题的解决方法 也是“见机行事”,根据原因的不同要用不同 ...