總目錄:Espresso從開始到…
只要使用了Espresso,那麼你一定不會對withId(R.id.xxx)
和withText(R.string.xxx)
這些ViewMatchers
感到陌生。實際上無論是ViewMatchers
、RootMatchers
亦或者是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所派生的封裝類:TypeSafeMatcher
和BoundedMatcher
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的類型增加了限制,所以在匹配實現中不需要關注類型項,只關注 對象的具體業務。
注意:
TypeSafeMatcher
和BoundedMatcher
使用時不要重寫matches
,matches
重寫後類別將失去本來的意義,如果需要重寫,請直接繼承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 |