[Espresso 4 Android Doc] 3. Espresso 基礎(chǔ)

聲明:本系列文章是對(duì) Android Testing Support Library官方文檔的翻譯粱年,水平有限了袁,歡迎批評(píng)指正酌儒。

1. Espresso 概覽
2. Espresso 設(shè)置說(shuō)明
3. Espresso 基礎(chǔ)
4. Espresso 備忘錄
5. Espresso 意圖
6. Espresso 高級(jí)示例
7. Espresso Web
8. AndroidJUnitRunner
9. ATSL 中的 JUnit4 規(guī)則
10. UI Automator
11. 可訪問(wèn)性檢查
Espresso API 鼓勵(lì)測(cè)試者以用戶會(huì)怎樣與應(yīng)用交互的方式進(jìn)行思考來(lái)定位 UI 元素并與它們交互轧叽。同時(shí)罗洗,框架不允許直接使用應(yīng)用的活動(dòng)和視圖肛跌,因?yàn)樵诜?UI 線程持有此類(lèi)對(duì)象并對(duì)它們操作是造成測(cè)試花屏的主要原因艺配。因此,你不會(huì)在 Espresso API 中看到諸如 getView 或 getCurrentActivity 等方法衍慎。但你仍然可以通過(guò)實(shí)現(xiàn) ViewActionViewAssertion 來(lái)對(duì)視圖進(jìn)行安全操作转唉。

以下是 Espresso 主要組件的概覽:

  • Espresso - 與視圖交互的切入點(diǎn)(參考 onViewonData)。也暴露了與任何視圖都沒(méi)有必然聯(lián)系的 API(如 ?pressBack)稳捆。
  • ViewMatchers - 實(shí)現(xiàn)了 ?Matcher<? super View>? 接口的對(duì)象集合赠法。你可以在 ?onView? 方法中傳入一個(gè)或多個(gè)此類(lèi)對(duì)象來(lái)在當(dāng)前的視圖結(jié)構(gòu)中定位一個(gè)視圖。
  • ViewActions - 可以作為參數(shù)傳入 ?ViewInteraction.perform()? 方法中的 ViewAction 的集合(如 ?click())乔夯。
  • ViewAssertions - 可以作為參數(shù)傳入 ?ViewInteraction.check()? 方法中的 ViewAssertion 的集合砖织。通常,你會(huì)使用帶有視圖匹配器的匹配斷言來(lái)判斷當(dāng)前被選中視圖的狀態(tài)末荐。

例如:

onView(withId(R.id.my_view))      // withId(R.id.my_view) is a ViewMatcher
  .perform(click())               // click() is a ViewAction
  .check(matches(isDisplayed())); // matches(isDisplayed()) is a ViewAssertion

使用 onView 查找視圖

多數(shù)情況下侧纯,onView 方法使用 hamcrest 匹配器以期望在當(dāng)前視圖結(jié)構(gòu)里匹配一個(gè)(唯一的)視圖。該匹配器十分強(qiáng)大而且對(duì)用過(guò) Mockito 或 JUnit 的人而言并不陌生甲脏。如果你對(duì) hamcrest 匹配器不熟悉眶熬,我們建議你先快速瀏覽一下此報(bào)告妹笆。(譯注:譯者本人表示打不開(kāi))

想要查找的視圖一般會(huì)有唯一的 ?R.id? 值,使用簡(jiǎn)單的 ?withId? 匹配器可以縮小搜索范圍娜氏。然而晾浴,當(dāng)你在測(cè)試開(kāi)發(fā)階段,無(wú)法確定 ?R.id值是合理的?牍白。例如脊凰,指定的視圖可能沒(méi)有 R.id? 值或該值不唯一。這將使一般的 instrumentation 測(cè)試變得脆弱而復(fù)雜茂腥,因?yàn)橥ㄓ玫墨@取視圖方式(通過(guò) findViewById())已經(jīng)不適用了狸涌。因此,你可能需要獲取持有視圖的私有對(duì)象 Activity 或 Fragment最岗,或者找到一個(gè)已知其 ?R.id? 值的父容器帕胆,然后在其中定位到特定的視圖。

Espresso 處理該問(wèn)題的方式很干脆般渡,它允許你使用已存在的或自定義的 ViewMatcher 來(lái)限定視圖查找懒豹。

通過(guò) ?R.id? 查找視圖:

onView(withId(R.id.my_view))

有時(shí),?R.id?值會(huì)被多個(gè)視圖共享驯用。此時(shí)脸秽,如果嘗試使用該 ?R.id? 值將會(huì)拋出類(lèi)似 ?AmbiguousViewMatcherException?的異常。異常信息會(huì)給你提供文字描述形式的當(dāng)前視圖結(jié)構(gòu)蝴乔,你可以搜索并找出所有使用非唯一 ?R.id? 值的視圖:

java.lang.RuntimeException:
com.google.android.apps.common.testing.ui.espresso.AmbiguousViewMatcherException:
This matcher matches multiple views in the hierarchy: (withId: is <123456789>)

...

+----->SomeView{id=123456789, res-name=plus_one_standard_ann_button, visibility=VISIBLE, width=523, height=48, has-focus=false, has-focusable=true, window-focus=true,
is-focused=false, is-focusable=false, enabled=true, selected=false, is-layout-requested=false, text=, root-is-layout-requested=false, x=0.0, y=625.0, child-count=1}
****MATCHES****
|
+------>OtherView{id=123456789, res-name=plus_one_standard_ann_button, visibility=VISIBLE, width=523, height=48, has-focus=false, has-focusable=true, window-focus=true,
is-focused=false, is-focusable=true, enabled=true, selected=false, is-layout-requested=false, text=Hello!, root-is-layout-requested=false, x=0.0, y=0.0, child-count=1}
****MATCHES****

通過(guò)查看視圖豐富的屬性记餐,你興許可以找到唯一可確認(rèn)的屬性(上例中,其中一個(gè)視圖有一個(gè)“Hello!”文本)薇正。你可以通過(guò)使用組合匹配器結(jié)合該屬性來(lái)縮小搜索范圍:

onView(allOf(withId(R.id.my_view), withText("Hello!")))

你也可以使用 ?not? 反轉(zhuǎn)匹配:

onView(allOf(withId(R.id.my_view), not(withText("Unwanted"))))

你可以在 ViewMatchers 類(lèi)中查看 Espresso 提供的視圖匹配器片酝。

注意:在一個(gè)良態(tài)的應(yīng)用中,所有用戶可與之交互的視圖都應(yīng)該包含說(shuō)明文字或有一個(gè)內(nèi)容描述(參考 Android 可訪問(wèn)性指導(dǎo))挖腰。如果你不能通過(guò)使用 ‘withText’ 或 ‘withContentDescripiton’ 來(lái)縮小 onView 的搜索范圍雕沿,可以認(rèn)為這是一個(gè)可訪問(wèn)性的 bug。

注意:請(qǐng)使用最少的匹配器來(lái)定位視圖猴仑。不要過(guò)指定审轮,因?yàn)檫@將強(qiáng)制框架做無(wú)用功。例如宁脊,如果一個(gè)視圖可以通過(guò)它的文字唯一確定断国,你不需要說(shuō)明該視圖也可以通過(guò) ?TextView? 指定。對(duì)許多視圖而言榆苞,使用它的 ?R.id? 值就足夠了稳衬。

注意:如果目標(biāo)視圖在一個(gè) ?AdapterView?(如 ?ListView?,?GridView?坐漏,?Spinner?)中薄疚,將不能使用 onView? 方法碧信,推薦使用 ?onData? 方法。

在視圖上執(zhí)行操作

當(dāng)為目標(biāo)視圖找到了合適的適配器后街夭,你將可以通過(guò) ?perform? 方法在該視圖上執(zhí)行 ?ViewAction?砰碴。

例如,點(diǎn)擊該視圖:

onView(…).perform(click());

你可以在一個(gè) perform 方法中執(zhí)行多個(gè)操作:

onView(…).perform(typeText("Hello"), click());

如果操作的視圖在 ?ScrollView?(水平或垂直方向)中板丽,需要考慮在對(duì)該視圖執(zhí)行操作(如 ?click()? 或 ?typeText()?)之前通過(guò) ?scrollTo()? 方法使其處于顯示狀態(tài)呈枉。這樣就保證了視圖在執(zhí)行其他操作之前是顯示著的。

onView(…).perform(scrollTo(), click());

注意:如果視圖已經(jīng)是顯示狀態(tài)埃碱,* *?scrollTo()? 將不會(huì)對(duì)界面有影響猖辫。因此,當(dāng)視圖的可見(jiàn)性取決于屏幕的大小時(shí)(例如砚殿,同時(shí)在大屏和小屏上執(zhí)行測(cè)試時(shí))啃憎,你可以安全的使用該方法。

你可以在 ViewActions 類(lèi)中產(chǎn)看 Espresso 提供的視圖操作似炎。

檢查一個(gè)視圖是否滿足斷言

斷言可以通過(guò) ?check()? 方法應(yīng)用在當(dāng)前選中的視圖上辛萍。最常用的是 ?matches()? 斷言,它使用一個(gè) ?ViewMatcher? 來(lái)判斷當(dāng)前選中視圖的狀態(tài)羡藐。

例如贩毕,檢查一個(gè)視圖擁有 “Hello!”文本:

onView(…).check(matches(withText("Hello!")));

注意:不要將 “assertions” 作為 onView 的參數(shù)傳入,而要在檢查代碼塊中明確指定你檢查的內(nèi)容传睹,例如:

如果你想要斷言視圖的內(nèi)容是 “Hello!” 耳幢,以下做法是反面教材:

// Don't use assertions like withText inside onView.
onView(allOf(withId(...), withText("Hello!"))).check(matches(isDisplayed()));

從另一個(gè)角度講,如果你想要斷言一個(gè)包含 “Hello!” 文本的視圖是可見(jiàn)的(例如欧啤,在修改了該視圖的可見(jiàn)性標(biāo)志之后),這段代碼是正確的启上。

注意:請(qǐng)留意斷言一個(gè)視圖沒(méi)有顯示和斷言一個(gè)視圖不在當(dāng)前視圖結(jié)構(gòu)之間的區(qū)別邢隧。

使用 onView 編寫(xiě)一個(gè)簡(jiǎn)單的測(cè)試

在此示例中,?SimpleActivity? 包含一個(gè) ?Button? 和一個(gè) ?TextView?冈在。當(dāng)點(diǎn)擊按鈕時(shí)倒慧,?TextView? 的內(nèi)容更改為 “Hello Espresso!”。以下是如何使用 Espresso 執(zhí)行此測(cè)試的講解:

1. 點(diǎn)擊按鈕

第一步是檢索一個(gè)能定位這個(gè)按鈕的屬性包券。?SimpleActivity? 中的這個(gè)按鈕擁有唯一的 ?R.id?纫谅,贊!

onView(withId(R.id.button_simple))

然后執(zhí)行點(diǎn)擊操作:

onView(withId(R.id.button_simple)).perform(click());

2. 檢查 ?TextView? 中是否包含 “Hello Espresso!”

待驗(yàn)證的 ?TextView? 也包含唯一的 ?R.id?:

onView(withId(R.id.text_simple))

然后驗(yàn)證文本內(nèi)容:

onView(withId(R.id.text_simple)).check(matches(withText("Hello Espresso!")));

?AdapterView? 控制器(ListView, GridView, ...)中使用 onData

?AdapterView? 是一個(gè)從適配器中動(dòng)態(tài)加載數(shù)據(jù)的特殊控件溅固。最常見(jiàn)的 ?AdapterView? 是 ListView?付秕。與像 ?LinearLayout? 這樣的靜態(tài)控件相反,在當(dāng)前視圖結(jié)構(gòu)中侍郭,可能只加載了 ?AdapterView? 子控件的一部分询吴, 簡(jiǎn)單的 ?onview()? 搜索不能找到當(dāng)前沒(méi)有被加載的視圖掠河。Espresso 通過(guò)提供單獨(dú)的 onData()? 切入點(diǎn)處理此問(wèn)題,它可以在操作適配器中有該問(wèn)題的條目或該條目的子項(xiàng)之前將其加載(使其獲取焦點(diǎn))猛计。

注意:你可能不會(huì)對(duì)初始狀態(tài)就顯示在屏幕上的適配器條目執(zhí)行 ?onData()? 加載操作唠摹,因?yàn)樗鼈円呀?jīng)被加載了。然而奉瘤,一直使用 ?onData()? 會(huì)更安全勾拉。

警告:對(duì)于 AdapterView? 的自定義實(shí)現(xiàn),如果他們打破了繼承契約(尤其是 ?getItem()? API)盗温,使用 ?onData()? 方法時(shí)會(huì)出現(xiàn)問(wèn)題藕赞。此種情況,最好是重構(gòu)你的應(yīng)用代碼肌访。如果不能這樣做找默,你可以實(shí)現(xiàn)一個(gè)匹配的自定義 ?AdapterViewProtocol?。查看 Espresso 提供的默認(rèn)的 AdapterViewProtocols 獲取供多信息吼驶。

使用 onData 編寫(xiě)一個(gè)簡(jiǎn)單的測(cè)試

這個(gè)簡(jiǎn)單的測(cè)試演示了如何使用? onData()?惩激。

?SimpleActivity? 包含一個(gè) ?Spinner? ,該 Spinner? 中有幾個(gè)條目——代表咖啡類(lèi)型的字符串蟹演。當(dāng)選中其中一個(gè)條目時(shí)风钻,?TextView? 內(nèi)容會(huì)變成 ?“One %s a day!”?,其中 %s 代表選中的條目酒请。此測(cè)試的目標(biāo)是打開(kāi) ?Spinner?骡技,選中一個(gè)條目然后驗(yàn)證 ?TextView? 中包含該條目。由于 ?Spinner? 類(lèi)基于 ?AdapterView?羞反,建議使用 ?onData()? 而不是 ?onView()? 來(lái)匹配條目布朦。

1. 點(diǎn)擊 Spinner 打開(kāi)條目選擇框

onView(withId(R.id.spinner_simple)).perform(click());

2. 點(diǎn)擊 “Americano” 條目

為了條目可供選擇,Spinner 用它的內(nèi)容創(chuàng)建了一個(gè) ?ListView?昼窗。該 ListView 可能會(huì)很長(zhǎng)是趴,而且它的元素不會(huì)出現(xiàn)在視圖結(jié)構(gòu)中。通過(guò)使用 ?onData()? 我們強(qiáng)制將想要得到的元素加入到視圖結(jié)構(gòu)中澄惊。Spinner 中的元素是字符串唆途,我們想要匹配的條目是字符串類(lèi)型并且值是 “Americano”。

onData(allOf(is(instanceOf(String.class)), is("Americano"))).perform(click());

3. 驗(yàn)證 TextView? 包含 “Americano” 字符串

onView(withId(R.id.spinnertext_simple).check(matches(withText(containsString("Americano"))));

調(diào)試

當(dāng)測(cè)試失敗時(shí)掸驱,Espresso 會(huì)提供有用的調(diào)試信息:

日志

Espresso 將所有視圖操作記錄到 logcat 中肛搬。例如:

ViewInteraction: Performing ‘single click’ action on view with text: Espresso

視圖結(jié)構(gòu)

當(dāng) onView()? 執(zhí)行失敗時(shí),Espresso 會(huì)在異常字符串里打印視圖結(jié)構(gòu)毕贼。

  • 如果 ?onView? 沒(méi)有找到目標(biāo)視圖温赔,會(huì)拋出 ?NoMatchingViewException?。你可以檢查異常字符串中的視圖結(jié)構(gòu)來(lái)分析為什么匹配器沒(méi)有匹配到視圖帅刀。
  • 如果 ?onView()? 根據(jù)給出的匹配器找到了多個(gè)視圖让腹,會(huì)拋出 ?AmbiguousViewMatcherException?远剩。視圖結(jié)構(gòu)會(huì)被打印出來(lái),并且所有被匹配的視圖都會(huì)帶有 MATCHES 標(biāo)簽:
java.lang.RuntimeException:
com.google.android.apps.common.testing.ui.espresso.AmbiguousViewMatcherException:
This matcher matches multiple views in the hierarchy: (withId: is <123456789>)

...

+----->SomeView{id=123456789, res-name=plus_one_standard_ann_button, visibility=VISIBLE, width=523, height=48, has-focus=false, has-focusable=true, window-focus=true,
is-focused=false, is-focusable=false, enabled=true, selected=false, is-layout-requested=false, text=, root-is-layout-requested=false, x=0.0, y=625.0, child-count=1}
****MATCHES****
|
+------>OtherView{id=123456789, res-name=plus_one_standard_ann_button, visibility=VISIBLE, width=523, height=48, has-focus=false, has-focusable=true, window-focus=true,
is-focused=false, is-focusable=true, enabled=true, selected=false, is-layout-requested=false, text=Hello!, root-is-layout-requested=false, x=0.0, y=0.0, child-count=1}
****MATCHES****

當(dāng)處理一個(gè)完整的視圖結(jié)構(gòu)或控件異常行為時(shí)骇窍,使用 Android 視圖結(jié)構(gòu)查看器有利于你給出說(shuō)明瓜晤。

?AdapterView? 提醒

Espresso 會(huì)提醒用戶 AdapterView 控件的出現(xiàn)。當(dāng) ?onView? 操作拋出 ?NoMatchingViewException? 異常而且 ?AdapterView? 控件在視圖結(jié)構(gòu)中時(shí)腹纳,最常見(jiàn)的解決方法是使用 onData()痢掠。異常信息中將會(huì)包含一個(gè)帶有一列適配器視圖的提醒。你可以通過(guò)此信息來(lái)調(diào)用 onData 加載目標(biāo)視圖嘲恍。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末足画,一起剝皮案震驚了整個(gè)濱河市,隨后出現(xiàn)的幾起案子佃牛,更是在濱河造成了極大的恐慌淹辞,老刑警劉巖,帶你破解...
    沈念sama閱讀 210,978評(píng)論 6 490
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件俘侠,死亡現(xiàn)場(chǎng)離奇詭異象缀,居然都是意外死亡,警方通過(guò)查閱死者的電腦和手機(jī)爷速,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 89,954評(píng)論 2 384
  • 文/潘曉璐 我一進(jìn)店門(mén)央星,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái),“玉大人惫东,你說(shuō)我怎么就攤上這事莉给。” “怎么了廉沮?”我有些...
    開(kāi)封第一講書(shū)人閱讀 156,623評(píng)論 0 345
  • 文/不壞的土叔 我叫張陵颓遏,是天一觀的道長(zhǎng)。 經(jīng)常有香客問(wèn)我滞时,道長(zhǎng)州泊,這世上最難降的妖魔是什么? 我笑而不...
    開(kāi)封第一講書(shū)人閱讀 56,324評(píng)論 1 282
  • 正文 為了忘掉前任漂洋,我火速辦了婚禮,結(jié)果婚禮上力喷,老公的妹妹穿的比我還像新娘刽漂。我一直安慰自己,他們只是感情好弟孟,可當(dāng)我...
    茶點(diǎn)故事閱讀 65,390評(píng)論 5 384
  • 文/花漫 我一把揭開(kāi)白布贝咙。 她就那樣靜靜地躺著,像睡著了一般拂募。 火紅的嫁衣襯著肌膚如雪庭猩。 梳的紋絲不亂的頭發(fā)上窟她,一...
    開(kāi)封第一講書(shū)人閱讀 49,741評(píng)論 1 289
  • 那天,我揣著相機(jī)與錄音蔼水,去河邊找鬼震糖。 笑死,一個(gè)胖子當(dāng)著我的面吹牛趴腋,可吹牛的內(nèi)容都是我干的吊说。 我是一名探鬼主播,決...
    沈念sama閱讀 38,892評(píng)論 3 405
  • 文/蒼蘭香墨 我猛地睜開(kāi)眼优炬,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼颁井!你這毒婦竟也來(lái)了?” 一聲冷哼從身側(cè)響起蠢护,我...
    開(kāi)封第一講書(shū)人閱讀 37,655評(píng)論 0 266
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤雅宾,失蹤者是張志新(化名)和其女友劉穎,沒(méi)想到半個(gè)月后葵硕,有當(dāng)?shù)厝嗽跇?shù)林里發(fā)現(xiàn)了一具尸體眉抬,經(jīng)...
    沈念sama閱讀 44,104評(píng)論 1 303
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 36,451評(píng)論 2 325
  • 正文 我和宋清朗相戀三年贬芥,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了吐辙。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 38,569評(píng)論 1 340
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡蘸劈,死狀恐怖昏苏,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情威沫,我是刑警寧澤贤惯,帶...
    沈念sama閱讀 34,254評(píng)論 4 328
  • 正文 年R本政府宣布,位于F島的核電站棒掠,受9級(jí)特大地震影響孵构,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜烟很,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 39,834評(píng)論 3 312
  • 文/蒙蒙 一颈墅、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧雾袱,春花似錦恤筛、人聲如沸。這莊子的主人今日做“春日...
    開(kāi)封第一講書(shū)人閱讀 30,725評(píng)論 0 21
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)。三九已至,卻和暖如春煎殷,著一層夾襖步出監(jiān)牢的瞬間屯伞,已是汗流浹背。 一陣腳步聲響...
    開(kāi)封第一講書(shū)人閱讀 31,950評(píng)論 1 264
  • 我被黑心中介騙來(lái)泰國(guó)打工豪直, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留劣摇,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 46,260評(píng)論 2 360
  • 正文 我出身青樓顶伞,卻偏偏與公主長(zhǎng)得像饵撑,于是被迫代替她去往敵國(guó)和親。 傳聞我的和親對(duì)象是個(gè)殘疾皇子唆貌,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 43,446評(píng)論 2 348

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