Espresso之Matcher

總目錄:Espresso從開始到…

只要使用了Espresso,那麼你一定不會對withId(R.id.xxx)withText(R.string.xxx)這些ViewMatchers感到陌生。實際上無論是ViewMatchersRootMatchers亦或者是Matchers,這些本質上都是Matcher。只是爲了方便不同的使用環境進行了封裝,本文將對Matcher以及這些封裝後的工具進行分析。

Matcher

所有的matcher都實現了接口Matcher

public interface Matcher<T> extends SelfDescribing {

    //遍歷當前視圖中的 view ,匹配則返回true,失敗返回false
	boolean matches(Object item);
    
    //生成本次匹配操作的描述信息
	void describeMismatch(Object item, Description mismatchDescription);
}

其中matches()是匹配的重點所在,在這裏執行匹配的操作。
但是在使用過程中,我們並沒有直接實現Matcher,而是實現其子類BaseMatcher所派生的封裝類:TypeSafeMatcherBoundedMatcher

1.TypeSafeMatcher

TypeSafeMatcher封裝後的最大的區別是,增加了 isInstance()檢查當前參加匹配的目標是否符合條件。我們看一下源碼,爲了便於區別和閱讀,只篩選出來一部分代碼:

public abstract class TypeSafeMatcher<T> extends BaseMatcher<T> {
    final private Class<?> expectedType;
    protected TypeSafeMatcher(Class<?> expectedType) {
        this.expectedType = expectedType;
    }
    protected abstract boolean matchesSafely(T item);
    public final boolean matches(Object item) {
        return item != null
                && expectedType.isInstance(item)
                && matchesSafely((T) item);
    }
    final public void describeMismatch(Object item, Description description) {
	......
    }
}

這裏我們可以清楚的看出和原始的BaseMatcher最大的區別是存儲了一個expectedType,在matchesSafely()判斷前會增加一個判斷,當前 item是否是expectedType的實例或者子類。這樣可以防止出現不同類型的恰好匹配的情況,相對於BaseMatcher是更加安全的。
我們看一個具體使用的栗子:

  public static Matcher<View> withId(final int id) {
    return new TypeSafeMatcher<View>() {
      ......
      @Override
      public void describeTo(Description description) {
        ......
      }
      @Override
      public boolean matchesSafely(View view) {
        ......
        return id == view.getId();
      }
    };
  }

這是我們常用的onView(withId(R.id.xxx)),看過前一篇文章我們應該知道onView()這裏需要的參數爲Matcher,所以我們一般使用時,會採用上面的方式書寫直接使用泛型。
代碼邏輯很簡單,就是依次獲取view的id,匹配我們輸入的 R.id.xxx,成功返回true,失敗返回false。
在這裏泛型是用於onView(),如果用於onData則需要根據 data 的類型類設置泛型。

2.BoundedMatcher

上文已經說到TypeSafeMatcher是在BaseMatcher的基礎上增加了類型的甄別,這裏BoundedMatcher就是在TypeSafeMatcher的基礎上在增加了一層安全保障。我們看一下源碼,爲了便於區別和閱讀,只篩選出來一部分代碼:

public abstract class BoundedMatcher<T, S extends T> extends BaseMatcher<T> {

  private final Class<?> expectedType;
  private final Class<?>[] interfaceTypes;

  public BoundedMatcher(Class<?> expectedType, Class<?> interfaceType1,
      Class<?>... otherInterfaces) {
    //將形參類型分別保存在expectedType和interfaceTypes中
    ......
    }
  }

  protected abstract boolean matchesSafely(S item);
  
  public final boolean matches(Object item) {
    if (item == null) {
      return false;
    }

    if (expectedType.isInstance(item)) {
      for (Class<?> intfType : interfaceTypes) {
        if (!intfType.isInstance(item)) {
          return false;
        }
      }
      return matchesSafely((S) item);
    }
    return false;
  }
}

原理上和TypeSafeMatcher時沒有任何區別的,只是條件上更加的嚴苛。這樣做還有其他的便捷之處,下面我們看個栗子:

  public static Matcher<View> withText(final Matcher<String> stringMatcher) {
    ......
    return new BoundedMatcher<View, TextView>(TextView.class) {
      @Override
      public void describeTo(Description description) {
        ......
      }
      @Override
      public boolean matchesSafely(TextView textView) {
        return stringMatcher.matches(textView.getText().toString());
      }
    };
  }

這裏直接對view的類型增加了限制,所以在匹配實現中不需要關注類型項,只關注 對象的具體業務。

注意:TypeSafeMatcherBoundedMatcher使用時不要重寫matchesmatches重寫後類別將失去本來的意義,如果需要重寫,請直接繼承BaseMatcher

ViewMatchers&RootMatchers&Matchers

這裏會將這幾部分的靜態函數陳列出來,看一遍有一些印象,在之後的使用過程中可以提高工作效率,至少不用有什麼想法都自己去自定義,可能已經有現成的輪子在那,況且自己做的輪子還不一定是圓的。(一下均爲個人理解,如有錯誤請指正)

1、ViewMatchers

函數 功能
assertThat() 用於生成斷言描述的工具
hasContentDescription() 匹配具有內容描述的view
hasDescendant() 匹配具有特定子視圖(直接或間接)的view
hasErrorText() 匹配getError爲特定字符串的EditView
hasFocus() 匹配獲取焦點的view
hasImeAction() 匹配支持輸入,並且具有特定IMEAction的view
hasLinks() 匹配具有超鏈接的TextView
hasSibling() 匹配具有特定相鄰view的view
isAssignableFrom() 匹配繼承自特定類的view
isChecked() 匹配實現Checkable接口並且處於選中狀態的View
isClickable() 匹配可以點擊的view
isCompletelyDisplayed() 匹配全部顯示在視圖中的view
isDescendantOfA() 匹配具有特定父視圖(直接或間接)的view
isDisplayed() 匹配顯示在視圖中(包括部分)的view
isDisplayingAtLeast() 匹配顯示在視圖中超過指定比值的view
isEnabled() 匹配當前可用(非灰色)的view
isFocusable() 匹配可以獲取焦點的view
isJavascriptEnabled() 匹配開啓JS的webView
isNotChecked() 匹配實現Checkable接口並且處於未選中狀態的View
isRoot() 匹配本身爲root的view
isSelected() 匹配被選中的view
supportsInputMethods() 匹配支持輸入的View
withChild() 匹配具有特定直接子視圖的view
withClassName() 匹配具有特定類名的view
withContentDescription() 匹配具有特定內容描述的view
withEffectiveVisibility() 匹配顯示在屏幕上(所有父視圖爲Visible)的view
withHint() 匹配getHint爲指定字符串的TextView
withId() 匹配具有指定id的view
withInputType() 匹配具有指定輸入類型的EditView
withParent() 匹配具有特定直接父視圖的view
withResourceName() 匹配具有指定資源名稱的view
withSpinnerText() 匹配getSeletedItem爲指定文本的view
withTagKey() 匹配getTag爲指定值的view
withText() 匹配getText爲指定字符串的TextView

2、RootMatchers

函數 功能
isDialog() 匹配是對話框的root
isFocusable() 匹配擁有焦點的root
isPlatformPopup() 匹配是彈出窗的root
isTouchable() 匹配可以觸摸的root
withDecorView() 匹配滿足特定條件的root

3、Matchers

函數 功能
allOf() 將所有matcher合併爲一個matcher(必須滿足所有matcher)
any() 生成一個判定是否爲指定類實例或者子類的matcher
anyOf() 將所有matcher合併爲一個matcher(滿足至少一個matcher即可)
anything() 生成一個匹配任何對象的matcher(matches寫死返回值爲true)
array() 由n個matcher生成一個可以對應匹配array[n],中每個data的matcher(必須依次對應)
arrayContaining() 由n個data生成一個可以對應匹配array[n],中每個data的matcher(必須依次對應)
arrayContainingInAnyOrder() 由n個data生成一個可以對應匹配array[n],中每個data的matcher(不必依次對應)
arrayWithSize() 生成匹配指定array.size()的matcher
both() 將兩個matcher合併成一個matcher
closeTo() 生成matcher匹配誤差範圍內的數:num∈[operand-error,operand+error]
comparesEqualTo() 生成matcher匹配指定value
contains() iterable中每一項符合對應matcher(必須依次匹配)
containsInAnyOrder() iterable中每一項符合對應matcher(不必依次匹配)
containsString() 包含特定string
describedAs() 更改matcher的描述
either() 指定對象與指定匹配器匹配時匹配
empty() collection爲空
emptyArray() 數組爲空
emptyCollectionOf() collection爲空
emptyIterable() Iterable爲空
emptyIterableOf() Iterable爲空
endsWith() String以指定字符串結尾
equalTo() 封裝equalTo
equalToIgnoringCase() string.equalTo()忽略大小寫
equalToIgnoringWhiteSpace() string.equalTo()忽略大小寫和留白
eventFrom() 匹配從指定source中派生的eventObject
everyItem() Iterable中任何一項都符合目標matcher
greaterThan() 大於指定值
greaterThanOrEqualTo() 大於等於指定值
hasEntry() 匹配指定Map<K , V>
hasItem() 匹配具有指定item的Iterable
hasItemInArray() 匹配具有指定item的數組
hasItems() 匹配具有指定多個item的Iterable
hasKey() 具有特定K 的Map
hasProperty() 具有指定名稱成員變量的對象
hasSize() Collection爲指定size
hasToString() 匹配toString爲指定值的對象
hasValue() 具有特定V的Map
hasXPath() Creates a matcher of {@link org.w3c.dom.Node}s that matches when the examined node contains a node at the specified xPath, with any content.
instanceOf() 爲特定class的實例或者子類
is() 封裝上文matcher:equalTo
isA() 封裝上文matcher:instanceOf
isEmptyOrNullStringv() ""或者空String
isEmptyString() “”(String)
isIn() 匹配指定Array中的item
isOneOf() 匹配列舉中的一項
iterableWithSize() iterable的size爲指定值
lessThan() 小於特定值
lessThanOrEqualTo() 小於等於特定值
not() 不匹配指定matcher
notNullValue() 不爲空值
nullValue() 空值
sameInstance() 對象相同的
samePropertyValuesAs() 具有相同屬性值
startsWith() String以特定字符串開始
stringContainsInOrder() 具有特定一個字符串的String
theInstance() 對象相同的與上文sameInstance相同
typeCompatibleWith() 當前class是繼承自指定class
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章