Robolectric使用(四)自定義

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的測試版本忘苛。

最后編輯于
?著作權歸作者所有,轉載或內容合作請聯系作者
  • 序言:七十年代末蝉娜,一起剝皮案震驚了整個濱河市,隨后出現的幾起案子扎唾,更是在濱河造成了極大的恐慌召川,老刑警劉巖,帶你破解...
    沈念sama閱讀 218,755評論 6 507
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件胸遇,死亡現場離奇詭異荧呐,居然都是意外死亡,警方通過查閱死者的電腦和手機狐榔,發(fā)現死者居然都...
    沈念sama閱讀 93,305評論 3 395
  • 文/潘曉璐 我一進店門坛增,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人薄腻,你說我怎么就攤上這事收捣。” “怎么了庵楷?”我有些...
    開封第一講書人閱讀 165,138評論 0 355
  • 文/不壞的土叔 我叫張陵罢艾,是天一觀的道長楣颠。 經常有香客問我,道長咐蚯,這世上最難降的妖魔是什么童漩? 我笑而不...
    開封第一講書人閱讀 58,791評論 1 295
  • 正文 為了忘掉前任,我火速辦了婚禮春锋,結果婚禮上矫膨,老公的妹妹穿的比我還像新娘。我一直安慰自己期奔,他們只是感情好侧馅,可當我...
    茶點故事閱讀 67,794評論 6 392
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著呐萌,像睡著了一般馁痴。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上肺孤,一...
    開封第一講書人閱讀 51,631評論 1 305
  • 那天罗晕,我揣著相機與錄音,去河邊找鬼赠堵。 笑死小渊,一個胖子當著我的面吹牛,可吹牛的內容都是我干的顾腊。 我是一名探鬼主播粤铭,決...
    沈念sama閱讀 40,362評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼杂靶!你這毒婦竟也來了梆惯?” 一聲冷哼從身側響起,我...
    開封第一講書人閱讀 39,264評論 0 276
  • 序言:老撾萬榮一對情侶失蹤吗垮,失蹤者是張志新(化名)和其女友劉穎垛吗,沒想到半個月后,有當地人在樹林里發(fā)現了一具尸體烁登,經...
    沈念sama閱讀 45,724評論 1 315
  • 正文 獨居荒郊野嶺守林人離奇死亡怯屉,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內容為張勛視角 年9月15日...
    茶點故事閱讀 37,900評論 3 336
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發(fā)現自己被綠了饵沧。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片锨络。...
    茶點故事閱讀 40,040評論 1 350
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖狼牺,靈堂內的尸體忽然破棺而出羡儿,到底是詐尸還是另有隱情,我是刑警寧澤是钥,帶...
    沈念sama閱讀 35,742評論 5 346
  • 正文 年R本政府宣布掠归,位于F島的核電站缅叠,受9級特大地震影響,放射性物質發(fā)生泄漏虏冻。R本人自食惡果不足惜肤粱,卻給世界環(huán)境...
    茶點故事閱讀 41,364評論 3 330
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望厨相。 院中可真熱鬧领曼,春花似錦、人聲如沸领铐。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,944評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽绪撵。三九已至,卻和暖如春祝蝠,著一層夾襖步出監(jiān)牢的瞬間音诈,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 33,060評論 1 270
  • 我被黑心中介騙來泰國打工绎狭, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留细溅,地道東北人。 一個月前我還...
    沈念sama閱讀 48,247評論 3 371
  • 正文 我出身青樓儡嘶,卻偏偏與公主長得像喇聊,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子蹦狂,可洞房花燭夜當晚...
    茶點故事閱讀 44,979評論 2 355

推薦閱讀更多精彩內容