Espresso 基礎
依賴的包:
espresso-core: 包括核心和基礎的view匹配器,動作,斷言。
espresso-web: 包括支持webview的資源。
espresso-idling-resource: espresso同步后台工作的機制。
espresso-contrib: 外部支持,包括日期選擇器,RecyclerView和繪制動作,斷言檢查,CountingIdlingResource。
espresso-intents: hermetic測試的有效擴展名和stub意圖。
添加依賴:
app/build.gradle
androidTestCompile 'com.android.support.test.espresso:espresso-core:2.2.2'
androidTestCompile 'com.android.support.test:runner:0.5'
android.defaultConfig下添加工具運行器
testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner"
Espresso主要組成部分:
ViewMatchers: ViewInteraction v = onView(ViewMatchers.withId(R.id.x))
ViewActions: v.perform(ViewActions.click())
click() | click on the view |
typeText() | click on a view and enters a specified string |
scrollTo() | scroll to the view, the target view must be subclassed from ScrollView and its property should be VISIBLE |
pressKey() | perform a key press using a specified keycode |
clearText() | clear the text in the target view |
ViewAssertions: v.check(ViewAssertions.matches(ViewMatchers.isDisplayed()))
AdapterView中使用onData(ListView, GridView...)
AdapterView 是一種特殊的類型,它從Adapter中動態地加載數據。AdapterView最通常的例子就是ListView。與LinerLayout靜態widgets相反,只有一部分AdapterView子view會被加載到當前的view層級中,通過onView進行查詢時當前未加載的view將不會被找到。Espresso通過onData()預先操作它或它的子類來處理即將加載的adapter的item。
初始化時就顯示在屏幕上的adapter中的view你也可以不使用onData()因為它們已經被加載了。然而,使用onData()會更加安全。
onData(allOf(is(instanceOf(String.class)), is("Americano"))).perform(click());
異步代碼測試
我們想要對異步代碼進行單元測試,首先要了解IdlingResource這個接口,接口需要我們實現的方式如下:
public interface IdlingResource { /** * 用來標識 IdlingResource 名稱 */ public String getName(); /** * 當前 IdlingResource 是否空閑 . */ public boolean isIdleNow(); /** * 注冊一個空閑狀態變換的ResourceCallback回調 */ public void registerIdleTransitionCallback(ResourceCallback callback); /** * 通知Espresso當前IdlingResource狀態變換為空閑的回調接口 */ public interface ResourceCallback { /** * 當前狀態轉變為空閑時,調用該方法告訴Espresso */ public void onTransitionToIdle(); } }
接下來看一下如何實現這個接口:
1 public final class SimpleCountingIdlingResource implements IdlingResource { 2 3 private final String resourceName; 4 5 private final AtomicInteger counter = new AtomicInteger(0); 6 7 private volatile ResourceCallback resourceCallback; 8 9 public SimpleCountingIdlingResource(String resourceName){ 10 11 this.resourceName = resourceName 12 } 13 14 15 @Override 16 public String getName() { 17 return this.resourceName; 18 } 19 20 @Override 21 public boolean isIdleNow() { 22 return counter.get() ==0; 23 } 24 25 @Override 26 public void registerIdleTransitionCallback(ResourceCallback resourceCallback){ 27 this.resourceCallback = resourceCallback; 28 } 29 30 //每當我們開始異步請求, 把counter 值+1 31 public void increment() { 32 counter.getAndIncrement(); 33 } 34 35 public void decrement() { 36 int counterVal = counter.decrementAndGet() 37 if(counterVal == 0) { 38 if(null != resourceCallback) { 39 resourceCallback.onTransitionToIdle(); 40 } 41 } 42 43 if(counterVal < 0) { 44 throw new IllegalArgumentException("Counter has been corrupted!"); 45 } 46 } 47 }
定義一個IdlingResource的管理類
public class EspressoIdlingResource { private static final String RESOURCE = "GLOBAL"; private static SimpleCountingIdlingResource mCountingIdlingResource = new SimpleCountingIdlingResource(RESOURCE); public static void increment() { mCountingIdlingResource.increment(); } public static void decrement() { mCountingIdlingResource.decrement(); } public static IdlingResource getIdlingResource() { return mCountingIdlingResource; } }
測試代碼
public class AsyncActivityTest { @Rule public ActivityTestRule<AsyncActivity> activityRule = new ActivityTestRule<>(AsyncActivity.class); private IdlingResource idlingresource; @Before public void setUp() throws Exception { idlingresource = activityRule.getActivity().getIdlingresource(); //注冊異步監聽,當該idlingresource中的counter標記值為0時才進行接下來的測試代碼 Espresso.registerIdlingResources(idlingresource); } @Test public void onLoadingFinished() throws Exception { // 不再需要這樣的代碼 // Thread.sleep(5000); // 未注冊idlingResource時,立即進行test,此時異步並未結束,報錯(tests failed) onView(withId(R.id.text)) .check(matches(withText("success!"))); } @After public void release() throws Exception { //我們在測試結束后取消注冊,釋放資源 Espresso.unregisterIdlingResources(idlingresource); } }
代碼執行邏輯應該是:
1,Activity中開始異步請求 (0 to 1)
2, 異步請求中... (1)
3, 異步請求結束 (1 to 0)
4, 執行IdlingResources中執行resourceCallback.onTransitionToIdle();
5, 測試代碼繼續進行
Espresso.registerIdlingResources(idlingresource) 和 Thread.sleep(5000) 這兩行代碼都是一樣的效果,阻塞住當前的測試,只不過前者更優越的是能夠在異步結束之后立馬執行接下來的測試代碼。