一個JUnit Rule就是一個實現了TestRule的類,這些類的做用相似於@Before
、@After
,是用來在每一個測試方法的執行先後執行一些代碼的一個方法。
若是你不清楚@Before
、@After
這些Annotation的意思,Chances are你還不瞭解Junit的使用,建議先看這篇文章。
那爲何不直接用這些annotation呢?這是由於它們都只能做用於一個類,若是同一個setup須要在兩個類裏面同時使用,那麼你就要在兩個測試類裏面定義相同的@Before
方法,而後裏面寫相同的代碼,這就形成了代碼重複。有的人說你能夠用繼承啊,首先我想說,我很討厭繼承這個東西,因此若是能夠不用繼承的話,我就不會用;再次我想說,若是你不討厭繼承的話,從如今開始,你也應該慢慢的討厭它了。
此外,JUnit Rule還能作一些@Before
這些Annotation作不到的事情,那就是他們能夠動態的獲取將要運行的測試類、測試方法的信息。這個在接下來的一個例子裏面能夠看到。java
不少測試框架好比JUnit、Mockito自帶給咱們不少已經實現過好了的JUnit Rule,咱們能夠直接拿來用。好比Timeout
,TemporaryFolder
,等等。這些Rule的使用方法很是簡單。定義一個這些類的public field,而後用@Rule
修飾一下就行了。好比android
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秒鐘以後若是方法尚未運行結束,就把結果標記爲失敗。
這裏須要注意的一點是Rule須要是public fieldgit
固然,若是隻能用框架自帶的Rule,那功能未免太受限了,JUnit Rule最強大的地方在於,咱們能夠本身寫知足咱們本身須要的Rule。因此如今的問題是怎麼寫這個Rule。簡單來講,寫一個Rule就是implement一個TestRule
interface,實現一個叫apply()
的方法。這個方法須要返回一個Statement
對象。下面給一個例子,這個 Rule的做用是,在測試方法運行以前,記錄測試方法所在的類名和方法名,而後在測試方法運行以後打印出來,至於怎麼在測試方法運行先後作這些事情,下面例子中的註釋裏面說的很清楚。github
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
修飾一下就行了。app
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);
}
}複製代碼
運行結果以下:框架
在右邊的框框能夠看到,把測試方法的方法名和所在的類名打印出來了。ide
上面的例子對於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
初始化:lua
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裏面。
獲取最新文章或想加入安卓單元測試交流羣,請關注下方公衆號