Android測試框架:Espresso


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) 這兩行代碼都是一樣的效果,阻塞住當前的測試,只不過前者更優越的是能夠在異步結束之后立馬執行接下來的測試代碼。

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM