Feature Toggle JUnit

  Feature Toggle,簡單來講,就是一個開關,將未完成功能的代碼屏蔽後發佈到生產環境,從而避免多分支的狀況。之因此有本文的產生,就是源於此情景。在引入Feature Toggle的同時,咱們發現以前對這些未開發完功能的代碼的單元測試不是很穩定,並且若是咱們在用feature toggle關掉這個功能以後,這些測試也是對發佈毫無價值可言,全部咱們須要將這些測試所有屏蔽掉,以避免影響運行其餘測試結果。app

  在通過項目組討論以後,咱們毅然決然摒棄了直接採用@Ignore的低級作法,決定本身來實現一個簡單的toggle,用annotation讀取配置文件的方式管理須要被屏蔽的測試。下面先介紹兩種方式來實現這一toggle:框架

  這兩種方法都必須定義一個annotation和配置文件,定義以下:ide

@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類中的代碼來lua

    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以前執行。spa

下面是如何本身寫一個類來實現TestRule歷來控制測試是否執行:code

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和配置文件,加上以下聲明:blog

public class FeatureToggleRuleTest {
    @Rule
    public TestRule rule = new FeatureToggleRule();

    @Test
    @FeatureToggle(feature = "feature")
    public void testTurnOn() throws Exception {
        fail();
    }
}

Rule更具備靈活性,而且功能不單單如此,咱們能夠在給本身的測試添加不少rule規則,這裏不進行深究。

 

總結:兩種方式,都實現同一個思想,用註解加配置文件來控制全部未完成功能測試部分的開關控制,並且簡單易行,而且能夠爲你們拓展更多的junit需求提供指導參考,謝謝。

相關文章
相關標籤/搜索