摘要:在答疑君的老師頁面,有一個老師搜索的功能。實際上,雖然方法是針對來進行測試的,但是在答疑君的測試腳本中,有時針對我也會采用方法直接去進行匹配,因為有些簡單的場景其實是不需要那么復雜的數(shù)據(jù)分析的,只關注于上的顯示我也能夠找到中的某個控件。
在之前的文章中,我們簡單介紹了Espresso的使用。通過onView()方法我們可以快速定位到界面上我們需要測試的目標元素。整體來說,onView()比較適用于UI比較簡單的情況,在不需要過于復雜的匹配條件的情況下是很方便的。但是,對于類似ListView這種有UI復用的元素來說,只是通過onView()就顯得復雜了一點,我們來看一下針對這種情況應有何種方案。
AdapterViewAdapterView是一種通過Adapter來動態(tài)加載數(shù)據(jù)的界面元素。我們常用的ListView, GridView, Spinner等等都屬于AdapterView。不同于我們之前提到的靜態(tài)的控件,AdapterView在加載數(shù)據(jù)時,可能只有一部分顯示在了屏幕上,對于沒有顯示在屏幕上的那部分數(shù)據(jù),我們通過onView()是沒有辦法找到的。
對于AdapterView,Espresso提供了如下方法用來查找元素:
/** * Creates an {@link DataInteraction} for a data object displayed by the application. Use this * method to load (into the view hierarchy) items from AdapterView widgets (e.g. ListView). * * @param dataMatcher a matcher used to find the data object. */ public static DataInteraction onData(Matcher extends Object> dataMatcher) {}
我們首先來研究一下這個方法的返回值。從以上定義可以看出,該方法返回了一個DataInteraction對象,還記得onView()方法返回的ViewInteraction對象么?這兩者的區(qū)別可以大概描述為:
ViewInteraction: 關注于已經(jīng)匹配到的目標控件。通過onView()方法我們可以找到符合匹配條件的唯一的目標控件,我們只需要針對這個控件進行我們需要的操作。
DataInteraction: 關注于AdapterView的數(shù)據(jù)。由于AdapterView的數(shù)據(jù)源可能很長,很多時候無法一次性將所有數(shù)據(jù)源顯示在屏幕上,因此我們主要先關注AdapterView中包含的數(shù)據(jù),而非一次性就進行View的匹配。
我們再來研究一下這個方法的入?yún)ⅰ囊陨隙x看出,該方法接收了一個Matcher extends Object>的參數(shù),該參數(shù)用來指定一個匹配規(guī)則。還記得onView()的入?yún)⒚矗渴且粋€Matcher
Matcher
Matcher extends Object>: 構造一個針對于Object(數(shù)據(jù))匹配的匹配規(guī)則。
從以上對比可以看出,我們在使用onData()方法對AdapterView進行測試的時候,我們的思路就轉變成了首先關注這個AdapterView的具體數(shù)據(jù),而不是UI上呈現(xiàn)的內(nèi)容。當然,我們最終的目標還是要找到目標的UI元素,但是我們是通過其數(shù)據(jù)源來進行入手的。
尋找數(shù)據(jù)那么,接下來,我們就要學習如何去尋找我們需要的數(shù)據(jù)了!顯然,要想找到我們需要的數(shù)據(jù),就需要構造一個onData()所使用的Matcher對象,而這個對象的構造和使用實際上和之前我們所用的針對于View的Matcher大概雷同。比如,我們可以指定單一條件:
onData(is(instanceOf(MyObject.class)))
表示我們需要找一個AdapterView,其數(shù)據(jù)源的類型是MyObject(這是一個自定義的類)。當然了,我們肯定還是需要更加精確地去尋找一個AdapterView中的指定條目,于是我們可以采用allOf()來構造一個符合匹配條件:
onData(allOf(is(instanceOf(MyObject.class)), myCustomMatcher()))
如上代碼便使用allOf()方法構造了一個符合匹配規(guī)則(allOf()方法可以參考第三篇文章Espresso入門里的介紹)。而上面的myCustomMatcher()方法構造了一個自定義的Matcher,我們可以采用自己的自定義Matcher來更加精準地進行數(shù)據(jù)的匹配。
自定義Matcher接下來我們要感受一下自定義Matcher的強大之處了!為了更好地給大家介紹自定義Matcher,我舉一個答疑君APP里面的例子來進行說明。
在答疑君APP的老師頁面,有一個老師搜索的功能。當我點擊搜索框時,界面上便會顯示之前的搜索關鍵字歷史。現(xiàn)在,我需要在這個搜索關鍵字列表中點擊相應的關鍵字來觸發(fā)搜索。
簡單來說,我的目的就是:在搜索歷史ListView中點擊搜索關鍵字為TEXT的條目。
首先,我的ListView的數(shù)據(jù)源類型為List
is(instanceOf(SearchItem.class))
這個構造條件就指定了列表的數(shù)據(jù)源為SearchItem類型。請注意,Espresso在根據(jù)onData()進行類型匹配時,是根據(jù)我們的Adapter.getItem()方法返回的數(shù)據(jù)類型進行匹配的。如果我們自己實現(xiàn)了一個自定義的Adapter,請注意我們構造的匹配規(guī)則要和getItem()方法返回的數(shù)據(jù)類型相統(tǒng)一。
接下來,我就需要去找那個含有TEXT關鍵字的數(shù)據(jù)項了。我的SearchItem類的定義極其簡單:
public class SearchItem { private String keyword; public SearchItem() {} public void setKeyword(String keyword) { this.keyword = keyword; } public String getKeyword() { return keyword; } }
接下來我只要找到那個keyword為TEXT的SearchItem數(shù)據(jù)項就可以了。為此,我構造了如下的一個自定義Matcher:
/** * 查找指定關鍵字的搜索條件 * @param name 需要搜索的關鍵字 */ public static Matcher
接下來對該方法做一些說明,以助于幫助大家構造自己的Matcher:
1. @return Matcher
很顯然,返回值必須是一個Matcher
2. BoundedMatcher
以上方法實際上是構造了一個BoundedMatcher,我們先來看一下BoundedMatcher的定義:
/** * Some matcher sugar that lets you create a matcher for a given type * but only process items of a specific subtype of that matcher. * * @paramThe desired type of the Matcher. * @param the subtype of T that your matcher applies safely to. */ public abstract class BoundedMatcherextends BaseMatcher { // ... protected abstract boolean matchesSafely(S item); // ... }
由以上定義我們可以看到,BoundedMatcher為我們指定了一個針對目標類型的子類型進行匹配的匹配規(guī)則。比如,我們現(xiàn)在需要一個Matcher
return new BoundedMatcher(SearchItem.class) {...}
3. matchesSafely()
上述復寫的matchesSafely()方法便是真正執(zhí)行匹配的地方了!大家可以看到,我由BoundedMatcher指定了SearchItem類型,因此matchesSafely()方法也接收了SearchItem類型的入?yún)ⅲ覀冎灰タ疾烊雲(yún)⑻峁┑倪@個SearchItem對象是否符合我們的匹配條件即可:
return item != null && !TextUtils.isEmpty(item.getKeyword()) && item.getKeyword().equals(name);
在如上代碼中,我做了三步檢查:
指定SearchItem本身不為null;
指定SearchItem的keyword不為空;
指定SearchItem的keyword和我們需要匹配的name相同。
只有符合這三個條件,我們才會認為當前的SearchItem數(shù)據(jù)項符合我們的預期。
綜合以上,將之前的兩個Matcher復合一下,我便可以構造如下的符合匹配規(guī)則了:
onData(allOf(is(instanceOf(SearchItem.class)), teacherSearchItemWithName(TEXT)))
這樣一來,我就能夠成功地在我的搜索歷史列表中找到關鍵字為TEXT的數(shù)據(jù)項了。
指定AdapterView這樣就完了嘛?是的,針對自定義Matcher就已經(jīng)講完了。實際上我在運行以上腳本的時候,Espresso還是給我報了個AmbiguousViewMatcherException的異常。這是因為,答疑君APP的布局比較復雜,在當前的View Hierarchy中有好幾個AdapterView,我需要指定我要進行匹配的AdapterView到底是哪一個。
Espresso提供了如下方法來完成這件事情:
/** * Selects a particular adapter view to operate on, by default we operate on any adapter view * on the screen. */ public DataInteraction inAdapterView(MatcheradapterMatcher){}
inAdapterView()可以讓我們指定我們需要匹配哪個AdapterView。我的搜索歷史列表的id為teacher_page_search_history_list,因此,我只要在上面的基礎上增加如下一行:
onData(allOf(is(instanceOf(SearchItem.class)), teacherSearchItemWithName(TEXT))) .inAdapterView(withId(R.id.teacher_page_search_history_list))
便解決了問題。現(xiàn)在,Espresso只會針對我的搜索歷史列表進行數(shù)據(jù)匹配了!
關于如何抉擇到目前為止,我們介紹了onView()和onData()的使用。從以上的文章中,相信大家也能夠感受到這兩種匹配思路的設計目的與區(qū)別。在我們平時的測試腳本編寫的過程中,我個人還是建議,一切都要按照我們自己的實際情況來進行方法的選擇。
實際上,雖然onData()方法是針對AdapterView來進行測試的,但是在答疑君的測試腳本中,有時針對AdapterView我也會采用onView()方法直接去進行匹配,因為有些簡單的場景其實是不需要那么復雜的數(shù)據(jù)分析的,只關注于UI上的顯示我也能夠找到ListView中的某個控件。話說回來,Espresso只是一個工具,至于具體如何去用,就看我們自己的發(fā)揮啦!
附錄Android自動化測試-從入門到入門(1) Hello Testing!
Android自動化測試-從入門到入門(2) Testing APIs
Android自動化測試-從入門到入門(3) Espresso入門
Android自動化測試-從入門到入門(4) uiautomatorviewer
Android自動化測試-從入門到入門(5) AdapterView的測試
Android自動化測試-從入門到入門(6) 會玩的Espresso
Android自動化測試-從入門到入門(7) UI Automator
文章版權歸作者所有,未經(jīng)允許請勿轉載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。
轉載請注明本文地址:http://specialneedsforspecialkids.com/yun/65420.html
摘要:附錄自動化測試從入門到入門自動化測試從入門到入門自動化測試從入門到入門入門自動化測試從入門到入門自動化測試從入門到入門的測試自動化測試從入門到入門會玩的自動化測試從入門到入門 之前的文章中,我們介紹了Android自動化測試的一些背景,以及Espresso的基本應用。除了之前介紹過的Espresso的相關用法,Espresso還提供了一些其他的用法,可以讓我們在不同場景下靈活使用。這篇...
摘要:右下角部分顯示當前選中控件的各個屬性。然后,向這個中輸入賬號信息就完成了一個表單的輸入。我們可以根據(jù)屬性區(qū)域顯示的來進行匹配賬號小總結所提供的界面簡單,使用方便,對于我們的自動化測試來說是一個很好的輔助工具。 我們用如下一行代碼來回顧一下之前介紹過的內(nèi)容: import static android.support.test.espresso.Espresso.onView; impo...
閱讀 617·2023-04-25 18:37
閱讀 2780·2021-10-12 10:12
閱讀 8315·2021-09-22 15:07
閱讀 564·2019-08-30 15:55
閱讀 3174·2019-08-30 15:44
閱讀 2194·2019-08-30 15:44
閱讀 1625·2019-08-30 13:03
閱讀 1560·2019-08-30 12:55