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需求提供指導參考,謝謝。