探索JUnit4擴展:使用Rule – JAVA編程語言程序開發技術文章

在上一篇文章《探索JUnit4擴展:擴展Runner》中,討論瞭一種擴展JUnit4的方式,即,直接修改Test Runner的實現(BlockJUnit4ClassRunner)。但這種方法顯然不便於靈活地添加或刪除擴展功能。本文將使用JUnit4.7才開始引入的擴展方式–Rule來實現相同的擴展功能。(2010.12.25最後更新)

1. Rule
Rule是JUnit4.7才開始提供的一種擴展方式,它能夠替代大部分已有的Runner擴展。JUnit包含兩種Rule Annotation:@ClassRule與@Rule。@ClassRule應用於測試類中的靜態變量,而@Rule應用於成員變量;相同地是,這些變量必須是TestRule接口的實例,且訪問修飾符必須為public。
在上篇博文中,對BlockJUnit4ClassRunner進行瞭擴展,被擴展的方法是methodBlock,現在我們來看看該方法體中的代碼,
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;
}但在BlockJUnit4ClassRunner中,possiblyExpectingExceptions(),withPotentialTimeout(),withBefores()和withAfters()都已經被標註為過時,JUnit建議使用Rule來替代這些方法的功能。

2. TestLogRule
如第1節所述,Rule Annotation要作用於TestRule接口的實例,那麼就要先創建一個TestRule的實現類。
public class TestLogRule implements TestRule {

    private static final DateFormat format = new SimpleDateFormat("yyyy-MM-dd_HH:mm:ss_SSS");

    @Override
    public Statement apply(Statement base, Description description) {
        TestLogger testLogger = description.getAnnotation(TestLogger.class);
        if (testLogger != null) {
            StringBuilder log = new StringBuilder(format.format(new Date()));
            log.append(" ").append(description.getClassName()).append("#")
                    .append(description.getMethodName()).append(": ")
                    .append(testLogger.log());
        System.out.println(log.toString());
    }

        return base;
    }
}如上所示,TestLogRule與上篇博文中的LoggedRunner的代碼有許多相同之處,功能則都是打印出指定的日志,每行日志又以當時的執行時間與完整方法名作為前綴。

3. 使用Rule的CalculatorTest
下面是新的測試類CalculatorTest,它將不使用BlockJUnit4ClassRunner的擴展LoggedRunner作為測試執行器,所以該類沒有使用@RunWith(LoggedRunner.class),那麼在執行該測試類時仍然會使用BlockJUnit4ClassRunner。
public class CalculatorTest {

    private static Calculator calculator = null;

    @Rule
    public TestLogRule testLogRule = new TestLogRule();

    @BeforeClass
    public static void createCalculator() {
        calculator = new Calculator();
    }

    @Test
    @TestLogger(log = "a simple pision")
    public void simpleDivide() {
        int value = calculator.pide(8, 2);
        Assert.assertTrue(value == 4);
    }

    @Test(expected = ArithmeticException.class)
    @TestLogger(log = "pided by zero, and an ArithmeticException thrown.")
    public void pidedByZero() {
        calculator.pide(8, 0);
    }
}與上篇博文中的CalculatorTest相比,本文中的CalculatorTest除瞭沒有使用LoggedRunner之外,還多瞭兩行代碼 www.aiwalls.com
@Rule
public TestLogRule testLogRule = new TestLogRule();在執行單元測試方法之前,BlockJUnit4ClassRunner會調用TestRule/TestLogRule中的apply()方法,即,會先打印出日志內容。

4. 小結
使用Rule對JUnit進行擴展,能夠避免對默認Runner的擴展,為測試類添加或移除Rule十分方便,而且Rule實現類本身也能很方便地被復用。

摘自 Jiangshachina
 

發佈留言