zoukankan      html  css  js  c++  java
  • Feature Toggle JUnit

      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需求提供指导参考,谢谢。

  • 相关阅读:
    精通特征工程
    reduce_mem_usage 降低内存使用 绘制学习率曲线和验证曲线
    正态性检验 Python正态性检验
    pd.melt Pandas 的melt的使用
    pandas dataframe 格式设置 set_option
    常用模块
    第9章 列表生成式、生成器和迭代器
    全栈作业(一)
    第8章 装饰器、模块和包
    第7章 Python 函数
  • 原文地址:https://www.cnblogs.com/Alex--Yang/p/3409540.html
Copyright © 2011-2022 走看看