Espresso是一個Google官方提供的Android應用UI自動化測試框架。Google希望,當Android的開發者利用Espresso寫完測試用例後,能一邊看著測試用例自動執行,一邊享受一杯香醇Espresso(濃咖啡)。
Espress有3個特點:
接下來,將從配置、寫用例、運行一步步介紹Espresso的使用。
0. 項目配置0.1 修改App的build.gradle在defaultConfig內增加,testInstrumentationRunner 「android.support.test.runner.AndroidJUnitRunner」,用來運行腳本
增加packagingOptions,避免編譯時候License的衝突
在dependencies中增加相關的引用(androidTestCompile只有在編譯測試用例時候才會運行,普通編譯不會)
下面是build.gradle中涉及到Espresso配置的內容
android { defaultConfig { testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner" } } packagingOptions { exclude 'LICENSE.txt' }}dependencies { // Espresso 相關的引用 compile 'com.android.support:support-annotations:22.1.1' androidTestCompile 'com.android.support:support-annotations:22.1.1' androidTestCompile('com.android.support.test.espresso:espresso-core:2.1'){ exclude group: 'javax.inject' } androidTestCompile 'com.android.support.test.espresso:espresso-intents:2.1' androidTestCompile 'com.android.support.test.espresso:espresso-contrib:2.1' androidTestCompile 'com.android.support.test:runner:0.2'}
0.2 添加TestRunner點擊頂欄菜單Run->Edit Configurations,出現如下的窗口後,點擊左上角的」+」,選擇」Android Tests」;
修改新Configuration的名字,選中App Module,輸入Runner,選擇」Show chooer dialog」,點擊」OK」完成
寫UI自動化測試用例,歸結起來就是3步:
定位View控制項
操作View控制項
校驗View控制項的狀態
對應Espresso,就是以下3個方法的調用:
onView(ViewMatcher) .perform(ViewAction) .check(ViewAssertion);
其中,onView是用來定位View控制項的,perform是操作控制項的,check是校驗View控制項的狀態。他們各自都需要再傳入對應的參數分別如下:
ViewMatcher,有withId、withText、withClassName等等方法來定位View控制項
ViewAction,有click()、longClick()、pressBack()、swipeLeft()等等方法來操作View控制項
ViewAssertion,有isEnabled()、isLeftOf()、isChecked()等等方法來校驗View控制項狀態
這裡有ViewMatcher、ViewAction、ViewAssertion的Cheat Sheet。
1.2 完整測試用例代碼這是一個非常簡單的測試用例,通過R.id.button定位控制項,對它調用了一下click,最後校驗控制項是不是enabled狀態。這裡面有一些註解,@Rule修飾的是被測試的Activity,@Test修飾的方法是測試用例。
@RunWith(AndroidJUnit4.class)public class MainActivityTest { @Rule public ActivityTestRule mActivityRule = new ActivityTestRule(MainActivity.class); @Test public void testTextViewDisplay() { onView(withId(R.id.button)) .perform(click()) .check(matches(isEnabled())); }}
1.3 注意Getting Started With Espresso 2.0這個視頻中提到了2個寫測試用例時的注意項:
避免Activity的層級跳轉,測試用例儘量只在單個Activity內完成。Activity層級跳轉越多,越容易出錯
強烈不推薦,直接獲取View的對象,調用View的方法來模擬用戶操作。應該統一使用Espresso提供的方法
測試用例,特別是UI自動化測試用例,應該儘量保持邏輯簡單,覆蓋關鍵路徑就足矣。因為UI變動是很頻繁的,越複雜,維護成本就越高,投入產出比就會自然降低了。
2. 運行用例在運行菜單中選擇步驟0.2中設置的TestRunner,點擊執行
測試用例模擬用戶操作自動運行
測試用例執行完成,在Android Studio的控制臺上,能看到如下的結果輸出
其中,看到」Done 3 of 3」標識,一共3個檢查點,都檢查通過了。如果有檢查不通過的話,右上角的綠色能量條會變成紅色。
3. 進階3.1 onData的使用對於ListView,如果要操作其中的某一個item,特別是不可見狀態的item,是不能通過上述的ViewMatch來定位的。我們都知道ListView的View是復用的,不可見狀態的item並沒有把內容繪製到View上。Espresso針對AdapterView(ListView的父類),提供了onData來支持。
onData(ObjectMatcher) .DataOptions .perform(ViewAction) .check(ViewAssertion);
onData傳入的是一個ObjectMather。首先假設ListView的Adapter中的Item的定義如下:
public static class Item { private final int value; public Item(int value) { this.value = value; } public String toString() { return String.valueOf(value); }}
下面定義一個withValue()的方法,返回一個BoundedMatcher。而其中的matchesSafely()方法是用來判斷match與否的,判斷的邏輯實現都放在這裡。
public static Matcher<Object> withValue(final int value) { return new BoundedMatcher<Object, MainActivity.Item>(MainActivity.Item.class) { @Override public void describeTo(Description description) { description.appendText("has value " + value); } @Override public boolean matchesSafely( MainActivity.Item item) { return item.toString().equals(String.valueOf(value)); } };}
有了上面的鋪墊,測試用例寫起來就水到渠成了。在id是R.id.list的AdapterView中找到數據項是27,然後執行click()操作。
@Testpublic void clickItem() { onData(withValue(27)) .inAdapterView(withId(R.id.list)) .perform(click()); //Do the assertion here.}
最後需要注意的是,onData()並不適用於RecyclerView,因為它不是繼承自AdapterView。Espresso提供專門給RecyclerView使用的RecyclerViewActions。
@Test public void clickItem() { onView(withId(R.id.recycler_view)) .perform( RecyclerViewActions.actionOnItemAtPosition(27, click()));}
3.2 Idling Resource的使用應用開發中很常見的一個場景是,點擊某個按鈕,發起網絡請求,等請求回來後解析數據,更新界面。Espresso針對這種測試場景,提供了原生的支持。
假設被測Activity初始化後有一個耗時的數據加載過程,activity.isSyncFinished()方法判斷數據加載是否已經完成。代碼如下:
@Overrideprotected void onCreate(Bundle savedInstanceState) { //模擬耗時的數據加載 new Handler(Looper.getMainLooper()).postDelayed(new Runnable() { @Override public void run() { mIsSyncFinished = true; } }, 5000);}private volatile boolean mIsSyncFinished = false;public boolean isSyncFinished() { return mIsSyncFinished;}
這種情況,Espresso提供了IdlingResource來保證數據加載完成了才開始執行測試用例代碼。首先,需實現IdlingResource接口:
getName()方法返回的String是作為註冊回調的Key,所以要確保唯一性
registerIdleTransitionCallback()的參數ResourceCallback會用做isIdleNow()時候的回調
isIdleNow()是否已經處於空閒狀態,這裡調用activity.isSyncFinished()方法來判斷數據加載是否完成
private static class MyIdlingResource implements IdlingResource { private ResourceCallback mCallback = null; private MainActivity mActivity; MyIdlingResource(MainActivity activity) { mActivity = activity; } @Override public String getName() { return "MyIdlingResource"; } @Override public void registerIdleTransitionCallback(ResourceCallback callback) { mCallback = callback; } @Override public boolean isIdleNow() { boolean isIdle = mActivity != null && mActivity.isSyncFinished(); if (isIdle && mCallback != null) { mCallback.onTransitionToIdle(); } return isIdle; }}
MyIdlingResource需要在恰當的時機註冊和反註冊。@Before和@After是依照JUnit4的慣例,分別在用例執行之前和之後去註冊和反註冊。那麼,如下測試用例執行的過程是:
測試用例啟動,註冊MyIdlingResource
啟動被測Activity
Activity初始化,啟動數據加載過程
Activity數據加載完成,執行測試用例方法testTextViewDisplay()
測試用例結束,反註冊MyIdlingResource
可見,IdlingResource能夠保證流轉到Idle狀態,才會執行測試代碼:
@Testpublic void testTextViewDisplay() { onView(withText("Show SnackBar")).check(ViewAssertions.matches(isDisplayed()));}@Beforepublic void registerIntentServiceIdlingResource() { Activity activity = mActivityRule.getActivity(); idlingResource = new MyIdlingResource((MainActivity) activity); Espresso.registerIdlingResources(idlingResource);}@Afterpublic void unregisterIntentServiceIdlingResource() { Espresso.unregisterIdlingResources(idlingResource);}
3.3. 執行原理本文開頭提到Espresso其中一個特點,無需主動寫Sleep等待UI事件的執行和UI的繪製。原因是,Espresso的用例運行過程是只有當UI線程IDLE和UI隊列沒有需要執行的事件時,Espresso的測試代碼才會被執行。使用方無需寫Sleep邏輯等待UI繪製完成。以下是Espresso測試用例執行簡易的流程圖,幫助理解:
引用官方介紹的一段話,Espresso的目標受眾是開發者。希望更多的團隊能夠實現Google的期許最大化利用Espresso,把Bug扼殺在搖籃中。
Target Audience
Espresso is targeted at developers, who believe that automated testing is an integral part of the development lifecycle. While it can be used for black-box testing, Espresso’s full power is unlocked by those who are familiar with the codebase under test.
Getting Started With Espresso 2.0:https://www.youtube.com/watch?v=TGU0B4qRlHY
Advanced Android Espresso:https://realm.io/news/chiu-ki-chan-advanced-android-espresso-testing/
Android Espresso 測試框架探究:http://blog.csdn.net/weijianfeng1990912/article/details/51540468
Android自動化測試-AdapterView的測試:https://segmentfault.com/a/1190000004392396
Android單元測試研究與實踐:http://tech.meituan.com/Android_unit_test.html