天天看點

Espresso常用腳本介紹

比對另一個視圖旁邊的視圖

布局可以包含其自身不是唯一的某些視圖(例如,聯系人表格中的重複調用按鈕可以具有相同的R.id,包含相同的文本并且具有與視圖層次結構内的其他調用按鈕相同的屬性)。

例如,在此活動中,文本為“7”的視圖在多行中重複:

Espresso常用腳本介紹

通常,非唯一視圖将與其旁邊的一些唯一标簽(例如,在呼叫按鈕旁邊的聯系人的名稱)配對。在這種情況下,您可以使用hasSibling比對器縮小您的選擇:

onView(allOf(withText("7"), hasSibling(withText("item: 0"))))
  .perform(click());      

比對ActionBar中的視圖

該ActionBarTestActivity有兩個不同的action Bar:一個正常的actionBar ,一個使用options menu建立的actionBar 。兩個操作欄都有一個總是可見的項目和兩個僅在溢出菜單中可見的項目。單擊項目時,會将TextView更改為所單擊項目的内容。

在兩個操作欄上比對可見圖示很容易:

public void testClickActionBarItem() {
  // We make sure the contextual action bar is hidden.
  onView(withId(R.id.hide_contextual_action_bar))
    .perform(click());

  // Click on the icon - we can find it by the r.Id.
  onView(withId(R.id.action_save))
    .perform(click());

  // Verify that we have really clicked on the icon by checking the TextView content.
  onView(withId(R.id.text_action_bar_result))
    .check(matches(withText("Save")));}      
Espresso常用腳本介紹

兩種看起來沒有什麼特别

public void testClickActionModeItem() {
  // Make sure we show the contextual action bar.
  onView(withId(R.id.show_contextual_action_bar))
    .perform(click());

  // Click on the icon.
  onView((withId(R.id.action_lock)))
    .perform(click());

  // Verify that we have really clicked on the icon by checking the TextView content.
  onView(withId(R.id.text_action_bar_result))
    .check(matches(withText("Lock")));}      
Espresso常用腳本介紹

單擊溢出菜單中的項目對于正常的操作欄有點棘手,因為一些裝置具有硬體溢出菜單按鈕(它們将打開選項菜單中的溢出項),并且一些裝置具有軟體溢出菜單按鈕(它們将打開正常溢出菜單)。幸運的是,Espresso為我們處理。

對于正常操作欄:

public void testActionBarOverflow() {
  // Make sure we hide the contextual action bar.
  onView(withId(R.id.hide_contextual_action_bar))
    .perform(click());

  // Open the overflow menu OR open the options menu,
  // depending on if the device has a hardware or software overflow menu button.
  openActionBarOverflowOrOptionsMenu(getInstrumentation().getTargetContext());

  // Click the item.
  onView(withText("World"))
    .perform(click());

  // Verify that we have really clicked on the icon by checking the TextView content.
  onView(withId(R.id.text_action_bar_result))
    .check(matches(withText("World")));}      
Espresso常用腳本介紹

這是在具有硬體溢出菜單按鈕的裝置上的外觀:

Espresso常用腳本介紹

對于上下文操作欄,它很容易再次:

public void testActionModeOverflow() {
  // Show the contextual action bar.
  onView(withId(R.id.show_contextual_action_bar))
    .perform(click());

  // Open the overflow menu from contextual action mode.
  openContextualActionModeOverflowMenu();

  // Click on the item.
  onView(withText("Key"))
    .perform(click());

  // Verify that we have really clicked on the icon by checking the TextView content.
  onView(withId(R.id.text_action_bar_result))
    .check(matches(withText("Key")));
  }      
Espresso常用腳本介紹

檢視完整的代碼:

​​​https://android.googlesource.com/platform/frameworks/testing/+/android-support-test/espresso/sample/src/androidTest/java/android/support/test/testapp/ActionBarTest.java​​

斷言不顯示視圖

執行一系列操作後,您肯定希望斷言測試中的UI的狀态。有時,這可能是一個不存在view在視圖層級中。請記住,您可以使用ViewAssertions.matches将任何hamcrest視圖比對器轉換為ViewAssertion。

onView(withId(R.id.bottom_left))
  .check(matches(not(isDisplayed())));      

如果視圖仍然是層次結構的一部分,上述方法适用。如果不是,你會得到一個NoMatchingViewException和你需要使用ViewAssertions.doesNotExist(見下文)。

斷言視圖不存在

如果視圖從視圖層次結構消失(例如,如果一個動作引起了過渡到另一個活動可能發生這種情況),你應該使用ViewAssertions.doesNotExist:

import static com.google.android.apps.common.testing.ui.espresso.Espresso.onView;
import static com.google.android.apps.common.testing.ui.espresso.assertion.ViewAssertions.doesNotExist;import static com.google.android.apps.common.testing.ui.espresso.matcher.ViewMatchers.withId;
onView(withId(R.id.bottom_left)).check(doesNotExist());      

斷言資料項不在擴充卡中

為了證明一個特定的資料項不在AdapterView中,你必須做一些不同的事情。我們必須找到我們感興趣的AdapterView,并詢問其持有的資料。我們不需要使用onData()。相反,我們使用onView來找到AdapterView,然後使用另一個比對器來處理視圖中的資料。

首先是比對器:

private static Matcher<View> withAdaptedData(final Matcher<Object> dataMatcher) {
  return new TypeSafeMatcher<View>() {

    @Override
    public void describeTo(Description description) {
      description.appendText("with class name: ");
      dataMatcher.describeTo(description);
    }

    @Override
    public boolean matchesSafely(View view) {
      if (!(view instanceof AdapterView)) {
        return false;
      }
      @SuppressWarnings("rawtypes")
      Adapter adapter = ((AdapterView) view).getAdapter();
      for (int i = 0; i < adapter.getCount(); i++) {
        if (dataMatcher.matches(adapter.getItem(i))) {
          return true;
        }
      }
      return false;
    }
  };}      

然後我們需要的是一個onView找到AdapterView:

@SuppressWarnings("unchecked")
public void testDataItemNotInAdapter(){
  onView(withId(R.id.list))
      .check(matches(not(withAdaptedData(withItemContent("item: 168")))));
  }      

如果在具有id清單的擴充卡視圖中存在等于“item:168”的項目,我們有一個斷言将失敗。

對于全樣本來看AdapterViewTest#testDataItemNotInAdapter。

使用自定義失敗處理程式

用自定義的替換Espresso的預設FailureHandler允許額外的(或不同的)錯誤處理 - 例如截取螢幕截圖或轉儲額外的調試資訊。

該CustomFailureHandlerTest示例示範如何實作自定義的故障處理程式:

private static class CustomFailureHandler implements FailureHandler {
  private final FailureHandler delegate;

  public CustomFailureHandler(Context targetContext) {
    delegate = new DefaultFailureHandler(targetContext);
  }

  @Override
  public void handle(Throwable error, Matcher<View> viewMatcher) {
    try {
      delegate.handle(error, viewMatcher);
    } catch (NoMatchingViewException e) {
      throw new MySpecialException(e);
    }
  }}      

此失敗處理程式抛出一個MySpecialException,而不是一個NoMatchingViewException,并将所有其他失敗委托給DefaultFailureHandler。CustomFailureHandler可以在測試的setUp()中注冊到Espresso:

@Override
public void setUp() throws Exception {
  super.setUp();
  getActivity();
  setFailureHandler(new CustomFailureHandler(getInstrumentation().getTargetContext()));}      

欲了解更多資訊,請參閱FailureHandler接口和​​Espresso.setFailureHandler​​。

使用inRoot來定位非預設視窗

Android支援多視窗。通常,這對于使用者和應用開發者來說是透明的(但是在某些情況下,多個視窗是可見的(例如,自動完成視窗被繪制在搜尋視窗中的主應用視窗上)。為了簡化,預設情況下,Espresso使用啟發式來猜測你打算與哪個Window互動。這個啟發式幾乎總是“足夠好”; 但是,在極少數情況下,您需要指定互動應定位哪個視窗。您可以通過提供自己的根視窗()比對:

onView(withText("South China Sea"))
  .inRoot(withDecorView(not(is(getActivity().getWindow().getDecorView()))))
  .perform(click());      

Take a look at the sample or the ​​sample​​ on GitHub.

比對作為ListView中的頁腳/标頭的視圖

頁眉和頁腳添加到通過清單視圖addHeaderView()/ addFooterView()的API。為了確定Espresso.onData()知道哪些資料對象來搭配,確定通過預設的資料對象值作為第二個參數

addHeaderView()/addFooterView()。例如:
public static final String FOOTER = "FOOTER";...View footerView = layoutInflater.inflate(R.layout.list_item, listView, false);
((TextView) footerView.findViewById(R.id.item_content)).setText("count:");
((TextView) footerView.findViewById(R.id.item_size)).setText(String.valueOf(data.size()));
listView.addFooterView(footerView, FOOTER, true);      

然後,你可以為頁腳寫一個比對器:

import static org.hamcrest.Matchers.allOf;
import static org.hamcrest.Matchers.instanceOf;
import static org.hamcrest.Matchers.is;
@SuppressWarnings("unchecked")
public static Matcher<Object> isFooter() {
  return allOf(is(instanceOf(String.class)), 
}      

并且在測試中加載視圖是容易的:

import static com.google.android.apps.common.testing.ui.espresso.Espresso.onData;import static com.google.android.apps.common.testing.ui.espresso.action.ViewActions.click;import static com.google.android.apps.common.testing.ui.espresso.sample.LongListMatchers.isFooter;
public void testClickFooter() {
  onData(isFooter())
    .perform(click());
  ...}      

​​sample連結位址:點選​​