谷歌2013年的時候開源了espress,谷歌的思路是,等到它足夠成熟和穩定以後,將其遷移到Android SDK中,以此可見對他的重視。Google使用Espresso測試了他們自己的超過30個應用程式,包括G+、Maps和Drive。
Espresso測試是非常容易實現的,由三步構成:
-
ViewMachers:尋找用來測試的View。
-
ViewActions:傳送互動事件。
-
ViewAssertions:檢驗測試結果
先看下官方給的示例,就能理解以上的三個步驟:
onView(withId(R.id.my_view)) // withId(R.id.my_view) is a ViewMatcher
.perform(click()) // click() is a ViewAction
.check(matches(isDisplayed())); // matches(isDisplayed()) is a ViewAssertion
複製程式碼
Espresso框架是google官方大力推薦的一套測試框架,所以無論如何都要學習一下的.另外,自Android Studio2.2版本開始,google就為Espresso框架內建了一個圖形化介面,用來自動生成單元測試程式碼。
接下來一起寫一demo測試,深入瞭解Espresso。
準備
支援Espresso:
dependencies {
...
testCompile 'junit:junit:4.12'
androidTestCompile 'com.android.support.test.espresso:espresso-core:2.2.2', {
exclude group: 'com.android.support', module: 'support-annotations'
}
}
複製程式碼
在dependencies中新增,一般預設會有testCompile 'junit:junit:4.12',所以我們只需新增另一句即可。
defaultConfig{
...
testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner"
}
複製程式碼
在defaultConfig中新增如上語句,支援測試執行。
建立Test類
特別注意,該類應在androidTest資料夾下
- androidTest:進行與Android相關(如呼叫Android裝置等)測試;
- test:進行簡單的只涉及java SE相關的測試。
舉個簡單例子:
@RunWith(AndroidJUnit4.class)
@LargeTest
public class MainActivityInstrumentationTest {
@Rule
public ActivityTestRule mActivityRule = new ActivityTestRule<>(
MainActivity.class);
@Test
public void sayHello(){
onView(withText("Say hello!")).perform(click());
onView(withId(R.id.textView)).check(matches(withText("Hello, World!")));
}
}
複製程式碼
- 首先需要在測試用例類的類體前新增@RunWith的註解,並設定測試執行平臺為AndroidJUnit4
- 如果允許測試需要較大消耗,可以使用@LargeTest註解
- 設定ActivityTestRule用來指明被測試的Activity,使用@Rule註解
- 測試方法必須以 test 開頭,並且使用@Test註解(否則會報找不到方法異常)
@Rule
@Rule
public ActivityTestRule mTestRule = new ActivityTestRule<>(MainActivity.class);
複製程式碼
這句話就定義了一個測試規則,可以看到構造方法的引數裡指定了一個 MainActivity.class, 具體的體現就是當你執行這段測試程式碼時,app將會直接開啟 MainActivity介面然後進行你所定義的測試用例。 所以當你想直接測試某個介面時,你可以把那個介面填到這個引數裡,這樣就直接開啟你指定的介面進行測試了。
@Test
@Test
public void testLogin() {
...
}
複製程式碼
定義一個測試方法,當你的測試類執行時,所執行的程式碼就是Test註解下的方法(Espresso還提供了其他的一些註解: 比如@After,@Before等,具體的用法可以去我上面寫的android官網上檢視),當然上面那段程式碼對應的就是testLogin測試方法,testLogin方法裡所定義的就是要測試的內容。
ViewMachers 查詢View
使用onView方法找到view:其中引數可以是withId(通過資源id查詢),withText(通過顯示內容查詢)有多個約束條件時,可以使用allOf 如allOf(withText("Hello") ,withId(R.id.hello))
注意:
- 無論是通過withId()找控制元件還是通過withText()找控制元件,或者其他方式比如 withClassName(),withResourceName(),withTagKey()等方法,都要一定保證你所找的控制元件在當前頁面確實存在且可見。
- 如果要測試AdapterView ,比如 ListView 或GridView等,使用上面的onView()方法是無效的,因為AdapterView的佈局item是動態呈現的,沒法直接指定,所以當要測試AdapterView時,請把onView()方法換成onData() 方法,與onView()方法返回ViewInteraction類似,onData()方法返回DataInteraction,二者用法基本都是一樣的。
ViewActions 執行事件
對View的操作:perform()方法 方式是onView(...).perform()。也可以執行多個操作在一個perform中如:perform(click(),clearText())。
所有的操作都有一個前提 ———— 就是要執行的view必須在當前介面上顯示出來(有且可見)。
方法名 | 含義 |
---|---|
click() | 點選view |
clearText() | 清除文字內容 |
swipeLeft() | 從右往左滑 |
swipeRight() | 從左往右滑 |
swipeDown() | 從上往下滑 |
swipeUp() | 從下往上滑 |
click() | 點選view |
closeSoftKeyboard() | 關閉軟鍵盤 |
pressBack() | 按下物理返回鍵 |
doubleClick() | 雙擊 |
longClick() | 長按 |
scrollTo() | 滾動 |
replaceText() | 替換文字 |
openLinkWithText() | 開啟指定超鏈 |
ViewAssertions 檢驗結果
使用check()方法來檢查View是否符合我們的期望: onView(...).check() 檢查view中是否含有文字“hello” check(matches(withText("hello")))
看下我寫的示例
我們基本所有的app都有登入功能,都需要呼入使用者名稱和密碼,那麼在點選登入之前需要對使用者名稱和密碼進行非空、格式等驗證。
以下示例我們點選登入按鈕時,首先對輸入的使用者名稱和密碼進行驗證,驗證不通過在TextView上顯示對應原因,驗證沒有問題顯示“登入成功”。
Activity介面及邏輯
@Override
public void onClick(View view) {
if (view.getId() == R.id.bt_login) {
login();
}
}
/**
* 去登入
*/
private void login() {
String name = et_name.getText().toString().trim();
String pwd = et_pwd.getText().toString().trim();
if (TextUtils.isEmpty(name)) {
tv_login_result.setText("使用者名稱為空");
return;
}
if (name.length() < 6 ) {
tv_login_result.setText("使用者名稱格式錯誤");
return;
}
if (TextUtils.isEmpty(pwd)) {
tv_login_result.setText("密碼為空");
return;
}
if (pwd.length() < 6 ) {
tv_login_result.setText("密碼格式錯誤");
return;
}
tv_login_result.setText("登入成功");
}
複製程式碼
其他程式碼忽略。
@RunWith(AndroidJUnit4.class)
@LargeTest
public class MainActivityTest {
private String[] names = {"", "a", "123123"};
private String[] pwds = {"", "a", "123123"};
@Rule
public ActivityTestRule mTestRule = new ActivityTestRule<>(MainActivity.class);
@Before
public void init() {
Log.e("TAG", "init: ");
}
@Test
public void testLogin() {
// 不做任何輸入,直接點選登入
onView(allOf(withId(R.id.bt_login), isDisplayed())).perform(click());
onView(allOf(withId(R.id.tv_login_result), isDisplayed())).check(matches(withText("使用者名稱為空")));
// 使用者名稱是空,點選登入
onView(allOf(withId(R.id.et_name), isDisplayed())).perform(replaceText(names[0]), closeSoftKeyboard());
onView(allOf(withId(R.id.bt_login), isDisplayed())).perform(click());
onView(allOf(withId(R.id.tv_login_result), isDisplayed())).check(matches(withText("使用者名稱為空")));
// 使用者名稱格式錯誤,點選登入
onView(allOf(withId(R.id.et_name), isDisplayed())).perform(replaceText(names[1]), closeSoftKeyboard());
onView(allOf(withId(R.id.bt_login), isDisplayed())).perform(click());
onView(allOf(withId(R.id.tv_login_result), isDisplayed())).check(matches(withText("使用者名稱格式錯誤")));
// 使用者名稱和密碼都正確,點選登入
onView(allOf(withId(R.id.et_name), isDisplayed())).perform(replaceText(names[2]), closeSoftKeyboard());
onView(allOf(withId(R.id.et_pwd), isDisplayed())).perform(replaceText(pwds[2]), closeSoftKeyboard());
onView(allOf(withId(R.id.bt_login), isDisplayed())).perform(click());
onView(allOf(withId(R.id.tv_login_result), isDisplayed())).check(matches(withText("登入成功")));
}
}
複製程式碼
這裡我們事先定義了一些測試資料,使用Espresso進行模擬各種情況輸入和點選,測試是否符合我們的預期:
對Espresso的介紹大概就是這些了,希望大家多提建議,一起進步。
獲取更多精彩內容,關注我的微信公眾號——Android機動車!