Extending Robolectric
Shadow Classes
Robolectric 定義了很多shadow classes用來修改和繼承Android Os的類的行為。當一個Android的類被實例化窜觉,Robolectric就會去尋找相對應的shadow類总棵,如果它找到了它就會去創(chuàng)建一個shadow對象來關聯到android的類脆淹。每一次方法被Android的類調用萌朱,Robolectric都會確保shadow類的相對應的方法被提前調用凝果。這會被應用到所有的方法汇在,無論是static的還是final的方法浓恳。
Adding Functionality
如果Robolectric提供的shadow 類沒有做你想要做的煮嫌,你可能需要為一個單元測試或者一個集成測試改變他們的行為笛谦。簡單的注冊一個類(我們叫他 shadowFoo)然后注解它(@Implements(Foo.class))。你的shadow類可以繼承Robolectric的shadows如果你想要的話昌阿。讓Robolectric知道你的shadow饥脑,使用@Config(shadows=ShadowFoo.class)或者創(chuàng)建一個名字是org.robolectric.Config.properties包含這行代碼(shadows=my.package.ShadowFoo)來注解你的測試方法或者類。
From Robolectric 2.0 on, the number of shadow classes needed is greatly reduced, because real Android OS code is present. Methods on your shadow class are able to call through to the Android OS code if you like, using Robolectric.directlyOn()
Shadow Classes
Shadow 類通常需要一個無參構造函數宝泵,可以讓Robolectric框架來實例化它好啰。他們關聯到在類聲明時使用@Implements注解的類上。通常他們需要一開始就被實現儿奶,那些shadow類的實施幾乎總是被移除框往,而且他們的數據成員很難被訪問。Shadow類的方法經常既shadow原始類的方法又通過設置返回值促進測試闯捎,又提供內部狀態(tài)的訪問椰弊,又記錄方法調用日志。
Shadow類需要模仿類的繼承結構瓤鼻,比如秉版,如果你為ViewGroup實現了shadow,ShadowViewGroup茬祷,然后你的shadow類需要繼承ViewGroup的超類的shadow 清焕, ShadowView。
@Implements(ViewGroup.class)
public class ShadowViewGroup extends ShadowView{
}
Methods
Shadow類實現和Android類具有同樣簽名的方法,Robolectric將在調用Android類的方法時調用shadow對象的具有同樣簽名的方法秸妥。
假如一個應用指定下面的代碼
this.imageView.setImageResource(R.drawable.pivotallabs_logo);
在測試時在Shadow對象中的ShadowImageView#setImageResource(int resId)方法將會被調用滚停。
Shadow方法必須被@Implementation注解標記,Robolectric包含有一個lint測試來幫助確保這個被正確的實施粥惧。
@Implements(ImageView.class)
public class ShadowImageView extends ShadowView{
@Implementation
public void setImageResource(int resId){
//implementation here
}
}
在關聯的Shdow類中實現原始類中已經定義的方法是很重要的键畴,否則Robolectric的尋找機制將會找不到他們(即使他們已經在Shadow subClass中聲明)。比如突雪,方法setEnabled() 已經在View中指定起惕,如果一個setEnabled()方法被在ShadowViewGroup中定義來替代ShadowView,然后在運行時即使當setEnabled()被一個ViewGroup的實例調用咏删,它也將不會被找到惹想。
Shadowing Constructors
一旦一個Shadow對象被實例化,當真實的對象的構造方法被調用的時候Robolectric將會尋找一個叫做constructor并且具有相同參數的方法饵婆。
舉例勺馆,如果應用調用TextView接受Context參數的構造方法:
new TextView(context);
Robolectric將會調用下面的接收Context參數的constrctor方法
@Implements(TextView.class)
public class ShadowTextView{
public void _constructor_(Context context){
this.context =context;
}
}
Getting access to the real instance
有時候,Shadow類可能會需要他們shadowing的對象的引用侨核,用來操作其中的屬性草穆,一個Shadow類可以通過聲明一個field注解@RealObject來完成。
@Implements(Point.class)
public class ShadowPoint{
@RealObject private Point realPoint;
public void _constructor_(int x,int y){
realPoint.x=x;
realPoint.y=y;
}
}
Robolectric將會在調用Point的真實對象的其他方法之前對realPoing進行設置搓译。
注意真實對象的方法的調用將會被Robolectric攔截并重定向是重要的悲柱,在測試代碼中并不經常被關注,但是它對Shadow類的實現具有重要的意義些己。
由于Shadow類的繼承結構不能總是反映出和Android類的關聯關系豌鸡,有時候需要通過他們的真實對象的調用,以便Robolectric在運行時將有機會將其轉發(fā)到正確的基于真實的類對象的shadow類段标。否則基類的Shadow類的方法將不能訪問子類的shadow的方法涯冠。
Creating Custom Shadows
自定義shadows是Robolectric的一個特性,允許你在對Android功能進行測試的時候進行有針對性的修改逼庞。這可以是捕獲的任何事情蛇更,簡單的方法調用,與測試對象交互插入代碼赛糟,或者什么都不做派任。
自定義shadows允許你僅僅在你的一些測試代碼中包含shadow功能,而不是在Robolectric源碼中添加或者修改一個Shadow璧南。
它也允許你的shadow涉及特定的domain掌逛,就像在你的測試類中的domain對象。
Writting a Custom Shadow
自定義Shadows和正常的shadow類是一樣的結構司倚。它們在類聲明時必須包含@Implements(AndroidClassName.class)注解豆混,你可以使用正常的shadow實現配置篓像,比如shadowing實例方法使用
==@Implementation== 或者shadowing構造方法使用 ==public void constructor(...)==。
@Implements(Bitmap.class){
@RealObject private Bitmap realBitmap;
private int bitmapQuality = -1;
@Implementation
public boolean compress(Bitmap.CompressFormat format,int quality,OutputStream stream){
bitmapQuality=quality;
return realBitmap.compress(format,quality,stream);
}
public int getQuality(){
return bitmapQuality;
}
}
Using a Custom Shadow
自定義Shadows通過在測試類或者測試方法使用@Config注解和使用 ==shadows== 數組屬性來和Robolectric獲得連接崖叫。
使用在上一節(jié)提到的MyShadowBitmap類遗淳,你需要在測試時注解 ==@Config(shadows=MyShadowBitmap.class)==,
在使用多個自定義shadow時:==@Config(shadows={MyShadowBitmap.class,MyOtherCustomShadow.class})==.
這樣使得Robolectric識別和執(zhí)行代碼的時候使用你自定義的shadow拍柒。
然而心傀,Robolectric.shadowOf()方法不能工作with自定義shadows,因為它不得不被Robolectric中每個Shadow 類實現拆讯。你可以用Robolectric.shadowOf_()代替它并強轉返回值到你實現的自定義shadow脂男。
另外,如果你選擇對一個已經在Robolectric中shadow過的android類進行shadow种呐,你將會替換這個Robolectric shadow宰翅。如果你仍然需要這個ji chu基礎shadow的功能,你可以嘗試繼承這個Robolectric shadow爽室。
Customizing the Test Runner
在有些情況下你想要自定義Robolectric的test runner用來在所有測試運行前或者在每個測試方法運行前執(zhí)行一些操作汁讼。一個很好的例子就是通過一組不同的依賴初始化一個依賴注入框架到你的測試。幸運的是阔墩,Robolectric有一個途徑來hook測試的生命周期嘿架,如果你在AndroidManifest.xml里指定了一個Application類,Robolectric將會自動嘗試加載一個你的application類的測試版本啸箫。舉例:
<application android:name=".FooApplication">
如果你使用了RoboGuice(google的依賴注入框架)耸彪,你需要初始化這個injector在你的Application類:
public class FooApplication extends Application{
@Override
public void onCreate(){
super.onCreate();
ApplicationModule module = new ApplicationModule();
setBaseApplicationInjector(this,DEFAULT_STAGE,new DefaultRoboModule(this),module);
}
}
你可以指定一個application的測試版本:
public class TestFooApplication extends FooApplication implements TestLifecycleApplication {
@Override
public void onCreate() {
super.onCreate();
TestApplicationModule module = new TestApplicationModule();
setBaseApplicationInjector(this, DEFAULT_STAGE, newDefaultRoboModule(this), module);
}
@Override
public void beforeTest(Method method) {
}
@Override
public void prepareTest(Object test) {
getInjector(this).injectMembers(test);
}
@Override
public void afterTest(Method method) {
}
}
當你測試時常常加載一組不同的bindings時,Robolectric將會加載這個application的測試版本忘苛。