安卓單元測試(八):Junit Rule的使用

JUnit Rule是什麼

一個JUnit Rule就是一個實現了TestRule的類,這些類的做用相似於@Before@After,是用來在每一個測試方法的執行先後執行一些代碼的一個方法。 html

若是你不清楚@Before@After這些Annotation的意思,Chances are你還不瞭解Junit的使用,建議先看這篇文章java

那爲何不直接用這些annotation呢?這是由於它們都只能做用於一個類,若是同一個setup須要在兩個類裏面同時使用,那麼你就要在兩個測試類裏面定義相同的@Before方法,而後裏面寫相同的代碼,這就形成了代碼重複。有的人說你能夠用繼承啊,首先我想說,我很討厭繼承這個東西,因此若是能夠不用繼承的話,我就不會用;再次我想說,若是你不討厭繼承的話,從如今開始,你也應該慢慢的討厭它了。 android

此外,JUnit Rule還能作一些@Before這些Annotation作不到的事情,那就是他們能夠動態的獲取將要運行的測試類、測試方法的信息。這個在接下來的一個例子裏面能夠看到。git

怎麼用JUnit Rule?

使用框架自帶的Rule

不少測試框架好比JUnit、Mockito自帶給咱們不少已經實現過好了的JUnit Rule,咱們能夠直接拿來用。好比TimeoutTemporaryFolder,等等。這些Rule的使用方法很是簡單。定義一個這些類的public field,而後用@Rule修飾一下就行了。好比github

public class ExampleTest {
    @Rule
    public Timeout timeout = new Timeout(1000);  //使用Timeout這個 Rule,

    @Test
    public void testMethod1() throws Exception {
        //your tests
    }
    
    @Test
    public void testMethod2() throws Exception {
        //your tests2
    }

    //other test methods
}

那麼,對於上面這個ExampleTest的每個測試方法。它們的運行時間都不能超過1秒鐘,否則就會標誌爲失敗。而它的實現方式就是在每一個方法測試以前都會記錄一下時間戳,而後開始倒計時,1秒鐘以後若是方法尚未運行結束,就把結果標記爲失敗。 app

這裏須要注意的一點是Rule須要是public field。框架

實現本身的Rule

固然,若是隻能用框架自帶的Rule,那功能未免太受限了,JUnit Rule最強大的地方在於,咱們能夠本身寫知足咱們本身須要的Rule。因此如今的問題是怎麼寫這個Rule。簡單來講,寫一個Rule就是implement一個TestRule interface,實現一個叫apply()的方法。這個方法須要返回一個Statement對象。下面給一個例子,這個 Rule的做用是,在測試方法運行以前,記錄測試方法所在的類名和方法名,而後在測試方法運行以後打印出來,至於怎麼在測試方法運行先後作這些事情,下面例子中的註釋裏面說的很清楚。jvm

public class MethodNameExample implements TestRule {
    @Override
    public Statement apply(final Statement base, final Description description) {
        return new Statement() {
            @Override
            public void evaluate() throws Throwable {
                //想要在測試方法運行以前作一些事情,就在base.evaluate()以前作
                String className = description.getClassName();
                String methodName = description.getMethodName();
                
                base.evaluate();  //這其實就是運行測試方法
                
                //想要在測試方法運行以後作一些事情,就在base.evaluate()以後作
                System.out.println("Class name: "+className +", method name: "+methodName);
            }
        };
    }
}

這個Rule這樣就算寫好了,如今來試試,用這個 Rule的方法跟使用自帶的Rule的用法是同樣的,寫一個public field,用@Rule修飾一下就行了。ide

public class ExampleUnitTest {
    @Rule
    public MethodNameExample methodNameExample = new MethodNameExample();
    @Test
    public void addition_isCorrect() throws Exception {
        assertEquals(4, 2 + 2);
    }

    @Test
    public void mulitiplication_isCorrect() throws Exception {
        assertEquals(4, 2 * 2);
    }
}

運行結果以下:單元測試

在右邊的框框能夠看到,把測試方法的方法名和所在的類名打印出來了。

上面的例子對於TestRule的實現應該說的比較清楚,可是看起來沒多大用。下面給另外的一個例子,你們或許會以爲這個東西更有用一點。在安卓裏面,咱們常常在不少地方須要用到Context這個東西。咱們的作法是將這個東西保存在一個類裏面,做爲靜態變量存在:

public class ContextHolder {
    private static Context sContext;

    public static void set(Context context) {
        sContext = context;
    }

    public static Context get() {
        return sContext;
    }
}

而後在自定義的Application#onCreate()裏面調一下ContextHolder.set()將這個context初始化。

若是你的當前項目是一個library,那麼你在測試環境下是沒有這個Application的,Robolectric會給你造一個Application,放在RuntimeEnvironment.application裏面。因此在測試環境下你可使用這個instance來將ContextHolder初始化。在這種狀況下,你就能夠用一個Rule來實現這樣的效果,在用到Context的測試方法運行以前將ContextHolder初始化:

public class ContextRule implements TestRule {
    @Override
    public Statement apply(Statement base, Description description) {
        ContextHolder.set(RuntimeEnvironment.application);
        return base;
    }
}

這樣,在運行一些用到context的測試方法以前,你就可使用這個Rule來給Context賦值了。

其實,最經常使用的Rule之一就是結合@Mock之類的Annotation快速的建立mock,可是這點我想做爲一篇單獨的文章寫一下。緣由之一是由於它涉及到的東西不只僅是Rule,還有其它的一些東西。更重要的緣由是,我但願你們能知道這個東西,而不是被這篇文章淹沒。請關注下一篇文章吧!

小結

JUnit Rule的介紹就到這裏,應該說比較簡單,倒是很是有幫助。但願這篇文章能幫助到你們瞭解這個東西。

這篇文章的代碼放在這個github repo裏面。

最後,若是你對安卓單元測試感興趣,歡迎加入咱們的交流羣,由於羣成員超過100人,沒辦法掃碼加入,請關注下方公衆號獲取加入方法。

相關文章
相關標籤/搜索