一個JUnit Rule就是一個實現了TestRule
的類,這些類的做用相似於@Before
、@After
,是用來在每一個測試方法的執行先後執行一些代碼的一個方法。 html
若是你不清楚@Before
、@After
這些Annotation的意思,Chances are你還不瞭解Junit的使用,建議先看這篇文章。 java
那爲何不直接用這些annotation呢?這是由於它們都只能做用於一個類,若是同一個setup須要在兩個類裏面同時使用,那麼你就要在兩個測試類裏面定義相同的@Before
方法,而後裏面寫相同的代碼,這就形成了代碼重複。有的人說你能夠用繼承啊,首先我想說,我很討厭繼承這個東西,因此若是能夠不用繼承的話,我就不會用;再次我想說,若是你不討厭繼承的話,從如今開始,你也應該慢慢的討厭它了。 android
此外,JUnit Rule還能作一些@Before
這些Annotation作不到的事情,那就是他們能夠動態的獲取將要運行的測試類、測試方法的信息。這個在接下來的一個例子裏面能夠看到。git
不少測試框架好比JUnit、Mockito自帶給咱們不少已經實現過好了的JUnit Rule,咱們能夠直接拿來用。好比Timeout
,TemporaryFolder
,等等。這些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,那功能未免太受限了,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人,沒辦法掃碼加入,請關注下方公衆號獲取加入方法。