Robolectric——Shadows 官網(wǎng)翻譯

Robolectric 通過(guò)創(chuàng)建一個(gè)包含真實(shí)Android 框架代碼的運(yùn)行時(shí)環(huán)境來(lái)進(jìn)行工作葫慎。 這意味著泻拦,當(dāng)你的測(cè)試或被測(cè)試代碼調(diào)用到Android框架時(shí)须眷,你會(huì)獲得更真實(shí)的體驗(yàn)黎炉,因?yàn)檫@跟在實(shí)際設(shè)備上執(zhí)行的大部分代碼都是相同的桃煎。然而還是有一些限制:

  1. Native code(源代碼) - Android源代碼不能在你的開(kāi)發(fā)機(jī)器上執(zhí)行。
    2.Out of process calls(進(jìn)程外調(diào)用) - 你的開(kāi)發(fā)機(jī)器上沒(méi)有運(yùn)行Android系統(tǒng)服務(wù)笤受。
    3.不足的測(cè)試Api - Android幾乎沒(méi)有適合測(cè)試的api穷缤。

Robolectric通過(guò)一組名字叫做Shadows的類(lèi)來(lái)解決這些問(wèn)題。每一個(gè)shadow 能拓展或繼承Android系統(tǒng)中對(duì)應(yīng)類(lèi)的行為箩兽。當(dāng)一個(gè)Android類(lèi)被實(shí)例化津肛,Robolectric會(huì)去查找一個(gè)對(duì)應(yīng)的shadow類(lèi),如果找到了汗贫,就會(huì)去創(chuàng)建一個(gè)與之關(guān)聯(lián)的shadow對(duì)象身坐。

通過(guò)使用字節(jié)碼工具秸脱,Robolectric能夠編寫(xiě)出跨平臺(tái)偽實(shí)現(xiàn)來(lái)替代原生代碼,并添加額外的api來(lái)實(shí)現(xiàn)測(cè)試部蛇。

名字是什么意思摊唇?
為什么叫“Shadow”?Shadow 對(duì)象們不是完全的Proxies, 不是完全的Fakes涯鲁,不是完全的Mocks或Stubs巷查。Shadows 有時(shí)是隱藏的,有時(shí)是可見(jiàn)的抹腿,并且可以引導(dǎo)你找到真正的對(duì)象岛请。至少我們沒(méi)有叫它們“sheep”,這是我們正在考慮的警绩。
Shadow Classes
Shadow Classes 總是需要一個(gè) public 類(lèi)型的無(wú)參構(gòu)造函數(shù)以便Robolectric框架可以實(shí)例化他們髓需。它們通過(guò)類(lèi)聲明上的@Implements注釋與它們所映射的類(lèi)相關(guān)聯(lián)。
Shadow 類(lèi)應(yīng)該模仿映射類(lèi)的繼承層次關(guān)系房蝉。例如僚匆,如果你實(shí)現(xiàn)一個(gè)ViewGroup的Shadow,ShadowViewGroup,然后你的Shadow類(lèi)需要繼承于ViewGroup的父類(lèi)Shadow,ShadowView搭幻。例如:

@Implements(ViewGroup.class)
public class ShadowViewGroup extends ShadowView{

Methods
Shadow 對(duì)象們實(shí)現(xiàn)了與Android類(lèi)中具有相同特征的方法咧擂。Robolectric將在調(diào)用Android對(duì)象上具有相同特征的方法時(shí)調(diào)用Shadow對(duì)象上的方法。

假設(shè)一個(gè)應(yīng)用程序定義了以下一行代碼:

this.imageView.setImageResource(R.drawable.pivotallabs_logo);

在測(cè)試中檀蹋,Shadow實(shí)例上的shadowimageview# setImageResource(int resId)方法將被調(diào)用松申。
Shadow方法必須用@Implementation注釋標(biāo)記。Robolectric包括一個(gè)校驗(yàn)測(cè)試俯逾,以確保測(cè)試正確進(jìn)行贸桶。

@Implements(ImageView.class)
public class ShadowImageView extends ShadowView{...
@Implementation
protected void setImageResource(intresId){// implementation here.}
}

Robolectric支持在原始類(lèi)上shadowing所有方法,包括private桌肴、static皇筛、final或native。

通常@Implementation方法也應(yīng)該有protected修飾符坠七。 這樣做的目的是減少shadows的API表面范圍;測(cè)試者應(yīng)該總是直接在Android框架類(lèi)上調(diào)用這些方法水醋。

重要的是,shadow方法是在最初定義它們的類(lèi)的相應(yīng)shadow上實(shí)現(xiàn)的彪置。 否則拄踪,Robolectric的查找機(jī)制將找不到它們(即使它們是在shadow子類(lèi)中聲明的)。舉個(gè)例子拳魁,setEnabled()方法定義在View里面惶桐。如果setEnabled()方法被定義在ShadowViewGroup而不是ShadowView,那么即使在一個(gè)實(shí)例化的ViewGroup上調(diào)用setEnabled(),它也不會(huì)在運(yùn)行時(shí)被找到姚糊。

Shadowing Constructors

一旦一個(gè)Shadow對(duì)象被實(shí)例化想虎,Robolectric將尋找一個(gè)名為constructor并帶有@Implementation注解的構(gòu)造方法,該方法與在真實(shí)對(duì)象上調(diào)用的構(gòu)造函數(shù)具有相同的參數(shù)叛拷。

例如,如果應(yīng)用程序代碼調(diào)用TextView構(gòu)數(shù)岂却,它接收一個(gè)Context:

new TextView(context);

Robolectric 將會(huì)調(diào)用如下constructor接收一個(gè)Context的方法:


@Implements(TextView.class)
public class ShadowTextView{
@Implementation
protected void __constructor__(Context context){
    this.context=context;
}

Getting access to the real instance(- 訪問(wèn)真實(shí)的實(shí)例)

有時(shí)忿薇,Shadow類(lèi)可能想要引用它們對(duì)應(yīng)類(lèi)的對(duì)象,例如躏哩,操作字段署浩。Shadow類(lèi)可以通過(guò)聲明一個(gè)帶注釋的@RealObject字段來(lái)實(shí)現(xiàn)這一點(diǎn):

@Implements(Point.class)
publicclassShadowPoint{
@RealObject
private Point realPoint;
public void __constructor__(intx,inty){
    realPoint.x=x;
    realPoint.y=y;
    }
}

在調(diào)用任何其他方法之前,Robolectric會(huì)將realPoint設(shè)置為Point的實(shí)際實(shí)例扫尺。

Custom Shadows(自定義)
Robolectric還在改進(jìn)中筋栋,我們依賴(lài)、歡迎并強(qiáng)烈鼓勵(lì)社區(qū)對(duì)bug修復(fù)和功能缺陷的貢獻(xiàn)正驻。然而,如果你想以一種不適合共享的方式修改陰影行為,或者你不能等待一個(gè)新版本包含一個(gè)關(guān)鍵的修復(fù)跛璧,我們支持自定義陰影纬向。

Writing a Custom Shadow(書(shū)寫(xiě)一個(gè)自定義的Shadows)
自定義的shadow結(jié)構(gòu)與普通的shadow類(lèi)非常相似。他們必須在類(lèi)的定義上包含@Implements(AndroidClassName.class)注解伤靠。你可以使用普通shadow的實(shí)現(xiàn)方式捣域,例如通過(guò)@Implementation標(biāo)記實(shí)現(xiàn)方法或者使用public void constructor(...)標(biāo)記構(gòu)造方法。如果你喜歡宴合,你可以讓你的shadow類(lèi)繼承于普通的 Robolectric shadows焕梅。

@Implements(Bitmap.class)
public class MyShadowBitmap {
    @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 Shadows
使用Shadows數(shù)組屬性,在測(cè)試類(lèi)或測(cè)試方法上使用@Config注釋將自定義shadow連接到Robolectric卦洽。要使用上一節(jié)中提到的MyShadowBitmap類(lèi)贞言,您需要用@Config(shadows={MyShadowBitmap.class})注釋有問(wèn)題的測(cè)試,如果包含多個(gè)自定義shadow:@Config(shadows={MyShadowBitmap.class, MyOtherCustomShadow.class})阀蒂。這使得Robolectric在對(duì)被隱藏的類(lèi)執(zhí)行代碼時(shí)能夠識(shí)別并使用自定義的陰影蜗字。

如果您希望將自定義shadow應(yīng)用于套件或某個(gè)包中的所有測(cè)試,則可以通過(guò)robolectric配置shadow robolectric.properties文件脂新。注意挪捕,默認(rèn)情況下shadows. shadowof()方法不適用于自定義陰影。相反争便,您可以使用Shadow.extract()并將返回值轉(zhuǎn)換為您實(shí)現(xiàn)的自定義Shadow類(lèi)级零。

Building a library of Custom Shadows.

如果您發(fā)現(xiàn)自己正在構(gòu)建一個(gè)自定義shadow庫(kù),那么您應(yīng)該考慮在您的shadow庫(kù)上運(yùn)行Robolectric的shadow注釋處理器。這提供了許多好處奏纪,例如

  1. 為你的每一個(gè)shadow生成shadowOf方法
  2. 生成一個(gè)ServiceLoader鉴嗤,這樣如果在類(lèi)路徑中找到自定義shadow,就會(huì)自動(dòng)應(yīng)用它
  3. 在teardown時(shí)調(diào)用任何static @Resetter方法序调,使您能夠重置靜態(tài)狀態(tài)醉锅。
  4. 對(duì)shadow執(zhí)行額外的驗(yàn)證和檢查。
android {
    defaultConfig {
        javaCompileOptions {
            annotationProcessorOptions {
                className 'org.robolectric.annotation.processing.RobolectricProcessor' arguments = [ 'org.robolectric.annotation.processing.shadowPackage' : 'com.example.myshadowpackage' ]
            }
        } 
    } 
}
dependencies { annotationProcessor project(":processor") }

最佳實(shí)踐:
Limit API surface area of shadows.
因?yàn)镽obolectric 3.7的@Implementation方法发绢,包括constructor方法可以被保護(hù)硬耍。這是可取的,因?yàn)闇y(cè)試代碼沒(méi)有業(yè)務(wù)調(diào)用這些方法边酒,通過(guò)使您的@Implementation方法受保護(hù)经柴,你鼓勵(lì)測(cè)試作者調(diào)用公共Android api代替。

Don’t use useinheritImplementationMethods
這通常是不必要的墩朦,在Robolectric 3.8中將被移除

不要在shadows中重寫(xiě) eaquls, hashCode 和 toString
避免這種情況坯认。要在測(cè)試中測(cè)試相等性以進(jìn)行比較,請(qǐng)選擇輔助程序庫(kù)或斷言庫(kù)氓涣。更喜歡添加一個(gè)describe()方法牛哺,而不是shadowing toString()

編寫(xiě)高質(zhì)量的shadow,以促進(jìn)測(cè)試行為而不是實(shí)現(xiàn)劳吠。
比起使用shadow作為美化的論據(jù)荆隘,更喜歡編寫(xiě)一個(gè)shadow來(lái)鼓勵(lì)測(cè)試行為。例如赴背,不要添加公開(kāi)已注冊(cè)偵聽(tīng)器的方法椰拒,而要為調(diào)用這些偵聽(tīng)器的方法添加@Implementation。

在shadow自己的代碼時(shí)要小心
Robolectric提供了大量的能力凰荚,這需要負(fù)責(zé)任的使用燃观。Shadows是測(cè)試與Android框架交互的理想工具,因?yàn)樵摽蚣懿恢С忠蕾?lài)注入便瑟,并且可以自由使用靜態(tài)代碼缆毁。在為自己的代碼編寫(xiě)自定shadow之前,請(qǐng)考慮是否不能更好地重構(gòu)代碼并使用流行的模仿庫(kù)(如Mockito)到涂。

—————————————————————————————————

個(gè)人總結(jié):
Robolectric通過(guò)Shadow的來(lái)映射Android 中的類(lèi)脊框,這樣就可以直接在單元測(cè)試中使用Android的類(lèi)。當(dāng)Robolectric中提供的Shadow沒(méi)有你需要的類(lèi)的時(shí)候践啄,比如你的自定義View的類(lèi)浇雹,在Robolectric中必然是沒(méi)有的,這個(gè)時(shí)候就可以使用自定義Shadow來(lái)實(shí)現(xiàn)屿讽。

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末昭灵,一起剝皮案震驚了整個(gè)濱河市,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌烂完,老刑警劉巖试疙,帶你破解...
    沈念sama閱讀 210,914評(píng)論 6 490
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場(chǎng)離奇詭異抠蚣,居然都是意外死亡祝旷,警方通過(guò)查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 89,935評(píng)論 2 383
  • 文/潘曉璐 我一進(jìn)店門(mén)嘶窄,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái)怀跛,“玉大人,你說(shuō)我怎么就攤上這事护侮。” “怎么了储耐?”我有些...
    開(kāi)封第一講書(shū)人閱讀 156,531評(píng)論 0 345
  • 文/不壞的土叔 我叫張陵羊初,是天一觀的道長(zhǎng)。 經(jīng)常有香客問(wèn)我什湘,道長(zhǎng)长赞,這世上最難降的妖魔是什么? 我笑而不...
    開(kāi)封第一講書(shū)人閱讀 56,309評(píng)論 1 282
  • 正文 為了忘掉前任闽撤,我火速辦了婚禮得哆,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘哟旗。我一直安慰自己贩据,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 65,381評(píng)論 5 384
  • 文/花漫 我一把揭開(kāi)白布闸餐。 她就那樣靜靜地躺著饱亮,像睡著了一般。 火紅的嫁衣襯著肌膚如雪舍沙。 梳的紋絲不亂的頭發(fā)上近上,一...
    開(kāi)封第一講書(shū)人閱讀 49,730評(píng)論 1 289
  • 那天,我揣著相機(jī)與錄音拂铡,去河邊找鬼壹无。 笑死,一個(gè)胖子當(dāng)著我的面吹牛感帅,可吹牛的內(nèi)容都是我干的斗锭。 我是一名探鬼主播,決...
    沈念sama閱讀 38,882評(píng)論 3 404
  • 文/蒼蘭香墨 我猛地睜開(kāi)眼失球,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼拒迅!你這毒婦竟也來(lái)了?” 一聲冷哼從身側(cè)響起,我...
    開(kāi)封第一講書(shū)人閱讀 37,643評(píng)論 0 266
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤璧微,失蹤者是張志新(化名)和其女友劉穎作箍,沒(méi)想到半個(gè)月后,有當(dāng)?shù)厝嗽跇?shù)林里發(fā)現(xiàn)了一具尸體前硫,經(jīng)...
    沈念sama閱讀 44,095評(píng)論 1 303
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡胞得,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 36,448評(píng)論 2 325
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了屹电。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片阶剑。...
    茶點(diǎn)故事閱讀 38,566評(píng)論 1 339
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖危号,靈堂內(nèi)的尸體忽然破棺而出牧愁,到底是詐尸還是另有隱情,我是刑警寧澤外莲,帶...
    沈念sama閱讀 34,253評(píng)論 4 328
  • 正文 年R本政府宣布猪半,位于F島的核電站,受9級(jí)特大地震影響偷线,放射性物質(zhì)發(fā)生泄漏磨确。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 39,829評(píng)論 3 312
  • 文/蒙蒙 一声邦、第九天 我趴在偏房一處隱蔽的房頂上張望乏奥。 院中可真熱鬧,春花似錦亥曹、人聲如沸邓了。這莊子的主人今日做“春日...
    開(kāi)封第一講書(shū)人閱讀 30,715評(píng)論 0 21
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)驶悟。三九已至,卻和暖如春材失,著一層夾襖步出監(jiān)牢的瞬間痕鳍,已是汗流浹背。 一陣腳步聲響...
    開(kāi)封第一講書(shū)人閱讀 31,945評(píng)論 1 264
  • 我被黑心中介騙來(lái)泰國(guó)打工龙巨, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留笼呆,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 46,248評(píng)論 2 360
  • 正文 我出身青樓旨别,卻偏偏與公主長(zhǎng)得像诗赌,于是被迫代替她去往敵國(guó)和親。 傳聞我的和親對(duì)象是個(gè)殘疾皇子秸弛,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 43,440評(píng)論 2 348

推薦閱讀更多精彩內(nèi)容