Android利用Espresso進(jìn)行UI自動(dòng)化測(cè)試的方法詳解
為什么需要UI自動(dòng)化測(cè)試?
我有一個(gè)觀點(diǎn),對(duì)于重復(fù)的工作,那么程序都是可以代替的,我想這是作為一個(gè)程序員的一個(gè)基本素養(yǎng)(能偷懶的絕不干活)。UI自動(dòng)化測(cè)試就是為了應(yīng)付一些重復(fù)的工作,比如說(shuō)測(cè)試某個(gè)功能,那么從應(yīng)用點(diǎn)擊,再經(jīng)過(guò)一系列的點(diǎn)擊頁(yè)面才能到達(dá)這個(gè)頁(yè)面,然后進(jìn)行測(cè)試,那么我們是不是可以寫段代碼讓app自動(dòng)跑起來(lái),自動(dòng)來(lái)到那個(gè)界面進(jìn)行測(cè)試呢?答案是肯定的,這就是本文所要說(shuō)的自動(dòng)化測(cè)試。
引言
谷歌2013年的時(shí)候開(kāi)源了espress,谷歌的思路是,等到它足夠成熟和穩(wěn)定以后,將其遷移到Android SDK中,以此可見(jiàn)對(duì)他的重視。Google使用Espresso測(cè)試了他們自己的超過(guò)30個(gè)應(yīng)用程序,包括G+、Maps和Drive。
Espresso測(cè)試是非常容易實(shí)現(xiàn)的,由三步構(gòu)成:
- ViewMachers:尋找用來(lái)測(cè)試的View。
- ViewActions:發(fā)送交互事件。
- ViewAssertions:檢驗(yàn)測(cè)試結(jié)果
先看下官方給的示例,就能理解以上的三個(gè)步驟:
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
Espresso框架是google官方大力推薦的一套測(cè)試框架,所以無(wú)論如何都要學(xué)習(xí)一下的.另外,自Android Studio2.2版本開(kāi)始,google就為Espresso框架內(nèi)置了一個(gè)圖形化界面,用來(lái)自動(dòng)生成單元測(cè)試代碼。
接下來(lái)一起寫一demo測(cè)試,深入了解Espresso。
準(zhǔn)備
支持Espresso:
dependencies {
...
testCompile 'junit:junit:4.12'
androidTestCompile 'com.android.support.test.espresso:espresso-core:2.2.2', {
exclude group: 'com.android.support', module: 'support-annotations'
}
}
在dependencies中添加,一般默認(rèn)會(huì)有testCompile 'junit:junit:4.12',所以我們只需添加另一句即可。
defaultConfig{
...
testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner"
}
在defaultConfig中添加如上語(yǔ)句,支持測(cè)試運(yùn)行。
創(chuàng)建Test類
特別注意,該類應(yīng)在androidTest文件夾下

- androidTest:進(jìn)行與Android相關(guān)(如調(diào)用Android設(shè)備等)測(cè)試;
- test:進(jìn)行簡(jiǎn)單的只涉及java SE相關(guān)的測(cè)試。
舉個(gè)簡(jiǎn)單例子:
@RunWith(AndroidJUnit4.class)
@LargeTest
public class MainActivityInstrumentationTest {
@Rule
public ActivityTestRule mActivityRule = new ActivityTestRule<>(
MainActivity.class);
@Test
public void sayHello(){
onView(withText("Say hello!")).perform(click());
onView(withId(R.id.textView)).check(matches(withText("Hello, World!")));
}
}
- 首先需要在測(cè)試用例類的類體前添加@RunWith的注解,并設(shè)置測(cè)試運(yùn)行平臺(tái)為AndroidJUnit4
- 如果允許測(cè)試需要較大消耗,可以使用@LargeTest注解
- 設(shè)置ActivityTestRule用來(lái)指明被測(cè)試的Activity,使用@Rule注解
- 測(cè)試方法必須以 test 開(kāi)頭,并且使用@Test注解(否則會(huì)報(bào)找不到方法異常)
@Rule
@Rule public ActivityTestRule mTestRule = new ActivityTestRule<>(MainActivity.class);
這句話就定義了一個(gè)測(cè)試規(guī)則,可以看到構(gòu)造方法的參數(shù)里指定了一個(gè) MainActivity.class, 具體的體現(xiàn)就是當(dāng)你運(yùn)行這段測(cè)試代碼時(shí),app將會(huì)直接打開(kāi) MainActivity界面然后進(jìn)行你所定義的測(cè)試用例。 所以當(dāng)你想直接測(cè)試某個(gè)界面時(shí),你可以把那個(gè)界面填到這個(gè)參數(shù)里,這樣就直接打開(kāi)你指定的界面進(jìn)行測(cè)試了。
@Test
@Test
public void testLogin() {
...
}
定義一個(gè)測(cè)試方法,當(dāng)你的測(cè)試類運(yùn)行時(shí),所執(zhí)行的代碼就是Test注解下的方法(Espresso還提供了其他的一些注解: 比如@After,@Before等,具體的用法可以去我上面寫的android官網(wǎng)上查看),當(dāng)然上面那段代碼對(duì)應(yīng)的就是testLogin測(cè)試方法,testLogin方法里所定義的就是要測(cè)試的內(nèi)容。
ViewMachers 查找View
使用onView方法找到view:其中參數(shù)可以是withId(通過(guò)資源id查找),withText(通過(guò)顯示內(nèi)容查找)有多個(gè)約束條件時(shí),可以使用allOf 如allOf(withText("Hello") ,withId(R.id.hello))
注意:
- 無(wú)論是通過(guò)withId()找控件還是通過(guò)
withText()找控件,或者其他方式比如withClassName(),withResourceName(),withTagKey()等方法,都要一定保證你所找的控件在當(dāng)前頁(yè)面確實(shí)存在且可見(jiàn)。 - 如果要測(cè)試AdapterView ,比如 ListView 或GridView等,使用上面的
onView()方法是無(wú)效的,因?yàn)锳dapterView的布局item是動(dòng)態(tài)呈現(xiàn)的,沒(méi)法直接指定,所以當(dāng)要測(cè)試AdapterView時(shí),請(qǐng)把onView()方法換成onData()方法,與onView()方法返回ViewInteraction類似,onData()方法返回DataInteraction,二者用法基本都是一樣的。
ViewActions 執(zhí)行事件
對(duì)View的操作:perform()方法 方式是onView(...).perform() 。也可以執(zhí)行多個(gè)操作在一個(gè)perform中如:perform(click(),clearText()) 。
所有的操作都有一個(gè)前提 ———— 就是要執(zhí)行的view必須在當(dāng)前界面上顯示出來(lái)(有且可見(jiàn))。
| 方法名 | 含義 |
|---|---|
| click() | 點(diǎn)擊view |
| clearText() | 清除文本內(nèi)容 |
| swipeLeft() | 從右往左滑 |
| swipeRight() | 從左往右滑 |
| swipeDown() | 從上往下滑 |
| swipeUp() | 從下往上滑 |
| click() | 點(diǎn)擊view |
| closeSoftKeyboard() | 關(guān)閉軟鍵盤 |
| pressBack() | 按下物理返回鍵 |
| doubleClick() | 雙擊 |
| longClick() | 長(zhǎng)按 |
| scrollTo() | 滾動(dòng) |
| replaceText() | 替換文本 |
| openLinkWithText() | 打開(kāi)指定超鏈 |
ViewAssertions 檢驗(yàn)結(jié)果
使用check()方法來(lái)檢查View是否符合我們的期望: onView(...).check() 檢查view中是否含有文本“hello” check(matches(withText("hello")))
看下我寫的示例
我們基本所有的app都有登錄功能,都需要呼入用戶名和密碼,那么在點(diǎn)擊登錄之前需要對(duì)用戶名和密碼進(jìn)行非空、格式等驗(yàn)證。
以下示例我們點(diǎn)擊登錄按鈕時(shí),首先對(duì)輸入的用戶名和密碼進(jìn)行驗(yàn)證,驗(yàn)證不通過(guò)在TextView上顯示對(duì)應(yīng)原因,驗(yàn)證沒(méi)有問(wèn)題顯示“登錄成功”。
Activity界面及邏輯
@Override
public void onClick(View view) {
if (view.getId() == R.id.bt_login) {
login();
}
}
/**
* 去登錄
*/
private void login() {
String name = et_name.getText().toString().trim();
String pwd = et_pwd.getText().toString().trim();
if (TextUtils.isEmpty(name)) {
tv_login_result.setText("用戶名為空");
return;
}
if (name.length() < 6 ) {
tv_login_result.setText("用戶名格式錯(cuò)誤");
return;
}
if (TextUtils.isEmpty(pwd)) {
tv_login_result.setText("密碼為空");
return;
}
if (pwd.length() < 6 ) {
tv_login_result.setText("密碼格式錯(cuò)誤");
return;
}
tv_login_result.setText("登錄成功");
}
其他代碼忽略。
@RunWith(AndroidJUnit4.class)
@LargeTest
public class MainActivityTest {
private String[] names = {"", "a", "123123"};
private String[] pwds = {"", "a", "123123"};
@Rule
public ActivityTestRule mTestRule = new ActivityTestRule<>(MainActivity.class);
@Before
public void init() {
Log.e("TAG", "init: ");
}
@Test
public void testLogin() {
// 不做任何輸入,直接點(diǎn)擊登錄
onView(allOf(withId(R.id.bt_login), isDisplayed())).perform(click());
onView(allOf(withId(R.id.tv_login_result), isDisplayed())).check(matches(withText("用戶名為空")));
// 用戶名是空,點(diǎn)擊登錄
onView(allOf(withId(R.id.et_name), isDisplayed())).perform(replaceText(names[0]), closeSoftKeyboard());
onView(allOf(withId(R.id.bt_login), isDisplayed())).perform(click());
onView(allOf(withId(R.id.tv_login_result), isDisplayed())).check(matches(withText("用戶名為空")));
// 用戶名格式錯(cuò)誤,點(diǎn)擊登錄
onView(allOf(withId(R.id.et_name), isDisplayed())).perform(replaceText(names[1]), closeSoftKeyboard());
onView(allOf(withId(R.id.bt_login), isDisplayed())).perform(click());
onView(allOf(withId(R.id.tv_login_result), isDisplayed())).check(matches(withText("用戶名格式錯(cuò)誤")));
// 用戶名和密碼都正確,點(diǎn)擊登錄
onView(allOf(withId(R.id.et_name), isDisplayed())).perform(replaceText(names[2]), closeSoftKeyboard());
onView(allOf(withId(R.id.et_pwd), isDisplayed())).perform(replaceText(pwds[2]), closeSoftKeyboard());
onView(allOf(withId(R.id.bt_login), isDisplayed())).perform(click());
onView(allOf(withId(R.id.tv_login_result), isDisplayed())).check(matches(withText("登錄成功")));
}
}
這里我們事先定義了一些測(cè)試數(shù)據(jù),使用Espresso進(jìn)行模擬各種情況輸入和點(diǎn)擊,測(cè)試是否符合我們的預(yù)期:

對(duì)Espresso的介紹大概就是這些了,希望大家多提建議,一起進(jìn)步。
總結(jié)
以上就是這篇文章的全部?jī)?nèi)容了,希望本文的內(nèi)容對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,如果有疑問(wèn)大家可以留言交流,謝謝大家對(duì)腳本之家的支持。
相關(guān)文章
AndroidStudio代碼達(dá)到指定字符長(zhǎng)度時(shí)自動(dòng)換行實(shí)例
這篇文章主要介紹了AndroidStudio代碼達(dá)到指定字符長(zhǎng)度時(shí)自動(dòng)換行實(shí)例,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。一起跟隨小編過(guò)來(lái)看看吧2020-03-03
android studio 3.6.1導(dǎo)入項(xiàng)目報(bào)錯(cuò)提示無(wú)法下載classpath里的內(nèi)容
這篇文章主要介紹了android studio 3.6.1導(dǎo)入項(xiàng)目報(bào)錯(cuò)提示無(wú)法下載classpath里的內(nèi)容,本文通過(guò)原因分析通過(guò)實(shí)例代碼給大家介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下2020-05-05
Android采用消息推送實(shí)現(xiàn)類似微信視頻接聽(tīng)
這篇文章主要為大家詳細(xì)介紹了Android采用消息推送實(shí)現(xiàn)類似微信視頻接聽(tīng),文中示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2019-11-11
EasyValidate優(yōu)雅地校驗(yàn)提交數(shù)據(jù)完整性
這篇文章主要介紹了EasyValidate優(yōu)雅地校驗(yàn)提交數(shù)據(jù)完整性,小編覺(jué)得挺不錯(cuò)的,現(xiàn)在分享給大家,也給大家做個(gè)參考。一起跟隨小編過(guò)來(lái)看看吧2018-12-12
Android ScreenLockReceiver監(jiān)聽(tīng)鎖屏功能示例
這篇文章主要介紹了Android ScreenLockReceiver監(jiān)聽(tīng)鎖屏功能,結(jié)合實(shí)例形式分析了Android監(jiān)聽(tīng)鎖屏及取消監(jiān)聽(tīng)功能的具體實(shí)現(xiàn)技巧,需要的朋友可以參考下2017-07-07
詳解Flutter自定義應(yīng)用程序內(nèi)鍵盤的實(shí)現(xiàn)方法
本文將展示如何利用Flutter創(chuàng)建自定義鍵盤小部件,用于在自己的應(yīng)用程序中的Flutter TextField中輸入文本,感興趣的小伙伴可以了解一下2022-06-06

