簡述:
1 >自動化測試Espresso最顯著的特點就是,可以與Ui互動。
2 >自動化測試Ui Automator可以與多個app進行互動
Ui Automator與現狀業界裡主流的Android自動化測試工具相比有什麼優缺點呢?
優點:
缺點:① 可以對所有操作進行自動化測試,操作簡單
② 不需要對被測程式進行重命名,且可測試所有裝置上的程式,如某APP,撥号、發資訊等
③ 對應控件定位,要比robotium簡單一點
① Ui Automator需要Android Level 16以上才可以使用,因為在Level 16及以上的API裡面才帶有UiAutomator工具
② 如果想要使用resource-id定位控件,則需要level 18及以上才可以
③ 對中文支援不好(不代表不支援,第三方jar可以實作)
3 >Ui Automator Viewer擷取頁面布局及控件屬性
Android Studio安裝及相關配置請參考Android Studio安裝與配置。
系統:Windows 7 64bit
Android系統:Android Studio 3.0.0.18
JDK:1.8.0_151
1、Espresso自動化測試
1 > 首先,在Android Studio 3.0中建立一個項目“EspressoTest”,建立方法參考Android Studio安裝與配置第3項。
同時删除自動生成的一些檔案,最終目錄結構如下:
2> 在app目錄下的build.gradle檔案中添加下面的引入,根據提示點選Sync Now
dependencies { implementation fileTree(dir: 'libs', include: ['*.jar']) implementation 'com.android.support:appcompat-v7:26.1.0' implementation 'com.android.support.constraint:constraint-layout:1.0.2' implementation 'com.android.support:support-annotations:26.1.0' implementation 'com.android.support:recyclerview-v7:26.1.0' testImplementation 'junit:junit:4.12' androidTestImplementation 'com.android.support.test:runner:1.0.1' androidTestImplementation 'com.android.support.test.espresso:espresso-core:3.0.1' androidTestImplementation 'com.android.support.test.espresso:espresso-core:3.0.1' androidTestImplementation('com.android.support.test.espresso:espresso-contrib:3.0.1') { transitive false } androidTestImplementation 'com.android.support.test.espresso:espresso-web:3.0.1' androidTestImplementation 'com.android.support:support-annotations:26.1.0' androidTestImplementation 'com.android.support:recyclerview-v7:26.1.0' }
3 > 編寫相關代碼
其中MainActivity界面如下,輸入框中點選數字後點選計算可以在結果處顯示兩者相加的值,點選webview跳轉到WebViewActivity,點選recycleview跳轉到RecycleViewActivity。
測試代碼:
MainActivityTest:
package com.hebbe.espressotest; import android.support.test.espresso.Espresso; import android.support.test.espresso.contrib.RecyclerViewActions; import android.support.test.espresso.matcher.ViewMatchers; import android.support.test.espresso.web.assertion.WebViewAssertions; import android.support.test.espresso.web.webdriver.DriverAtoms; import android.support.test.espresso.web.webdriver.Locator; import android.support.test.rule.ActivityTestRule; import android.support.test.runner.AndroidJUnit4; import org.hamcrest.Matchers; import org.junit.Rule; import org.junit.Test; import org.junit.runner.RunWith; import com.hebbe.espressotest.MainActivity; import com.hebbe.espressotest.R; import static android.support.test.espresso.action.ViewActions.click; import static android.support.test.espresso.action.ViewActions.closeSoftKeyboard; import static android.support.test.espresso.action.ViewActions.typeText; import static android.support.test.espresso.assertion.ViewAssertions.matches; import static android.support.test.espresso.matcher.ViewMatchers.isDisplayed; import static android.support.test.espresso.matcher.ViewMatchers.withId; import static android.support.test.espresso.matcher.ViewMatchers.withText; import static android.support.test.espresso.web.sugar.Web.onWebView; import static android.support.test.espresso.web.webdriver.DriverAtoms.findElement; import static android.support.test.espresso.web.webdriver.DriverAtoms.webClick; import static org.junit.Assert.*; /** * Created by Hebbe on 2018/1/15. */ @RunWith(AndroidJUnit4.class) public class MainActivityTest { @Rule public ActivityTestRule activityTestRule = new ActivityTestRule(MainActivity.class); @Test public void test() { //通過id找到edittext,在裡面輸入2并關閉輸入法 Espresso.onView(withId(R.id.editText)).perform(typeText("2"),closeSoftKeyboard()); //通過id找到edittext,在裡面輸入5并關閉輸入法 Espresso.onView(withId(R.id.editText2)).perform(typeText("5"), closeSoftKeyboard()); //通過id找到button,執行點選事件 Espresso.onView(withId(R.id.button)).perform(click()); //通過id找到textview,并判斷是否與文本比對 //Espresso.onView(withId(R.id.textView)).check(matches(withText("計算結果:6"))); Espresso.onView(withId(R.id.textView)).check(matches(withText("計算結果:7"))); } @Test public void testRecycleView() { //通過文本RecycleView找到按鈕,并執行點選事件,跳轉到RecycleviewActivity Espresso.onView(withText("RecycleView")).perform(click()); //通過文本"Item 0"找到view,并檢查是否顯示,然後執行點選事件,此時會彈出對話框 Espresso.onView(withText("Item 0")).check(matches(isDisplayed())).perform(click()); //通過文本"确定"找到對話框上的确定按鈕,執行點選事件,關閉對話框 Espresso.onView(withText("确定")).perform(click()); //通過文本"Item 2"找到view,并檢查是否顯示,然後執行點選事件,此時會彈出對話框 Espresso.onView(withText("Item 2")).check(matches(isDisplayed())).perform(click()); //執行點選傳回按鈕事件,關閉對話框 Espresso.pressBack(); //通過id找到recycleview,然後執行滑動事件,滑動到21項 Espresso.onView(ViewMatchers.withId(R.id.recycleview)).perform(RecyclerViewActions.scrollToPosition(21)); //通過文本"Item 20"找到view,并檢查是否顯示,然後執行點選事件,此時會彈出對話框 Espresso.onView(withText("Item 20")).check(matches(isDisplayed())).perform(click()); //通過文本"确定"找到對話框上的确定按鈕,執行點選事件,關閉對話框 Espresso.onView(withText("确定")).perform(click()); //執行點選傳回按鈕事件,關閉跳轉到RecycleviewActivity Espresso.pressBack(); } @Test public void testWebView() { //通過文本RecycleView找到按鈕,并執行點選事件,跳轉到WebViewActivity Espresso.onView(withText("WebView")).perform(click()); //通過name為"word"找到搜尋輸入框 onWebView().withElement(findElement(Locator.NAME, "word")) //往輸入框中輸入字元串"android" .perform(DriverAtoms.webKeys("android")) //通過id為"index-bn"找到"百度一下"按鈕 .withElement(DriverAtoms.findElement(Locator.ID, "index-bn")) //執行點選事件 .perform(webClick()) //通過id為"results"找到結果div .withElement(DriverAtoms.findElement(Locator.ID, "results")) //檢查div中是否包含字元串"android" .check(WebViewAssertions.webMatches(DriverAtoms.getText(), Matchers.containsString("android"))); //執行點選傳回按鈕事件,關閉跳轉到WebViewActivity Espresso.pressBack(); } }
WebViewActivityTest:
package com.hebbe.espressotest; import android.content.Intent; import android.support.test.espresso.web.action.AtomAction; import android.support.test.espresso.web.assertion.WebViewAssertions; import android.support.test.espresso.web.sugar.Web; import android.support.test.espresso.web.webdriver.DriverAtoms; import android.support.test.espresso.web.webdriver.Locator; import android.support.test.rule.ActivityTestRule; import android.support.test.runner.AndroidJUnit4; import org.hamcrest.Matchers; import org.junit.Rule; import org.junit.Test; import org.junit.runner.RunWith; import com.hebbe.espressotest.WebViewActivity; import static android.support.test.espresso.web.sugar.Web.onWebView; import static android.support.test.espresso.web.webdriver.DriverAtoms.findElement; import static android.support.test.espresso.web.webdriver.DriverAtoms.webClick; import static org.hamcrest.Matchers.containsString; import static org.junit.Assert.*; /** * Created by Hebbe on 2018/1/15. */ @RunWith(AndroidJUnit4.class) public class WebViewActivityTest { @Rule public ActivityTestRule activityTestRule = new ActivityTestRule(WebViewActivity.class, false); @Test public void test() { //傳遞資料到WebViewActivity Intent intent = new Intent(); intent.putExtra(WebViewActivity.EXTRA_URL, "http://www.baidu.com"); activityTestRule.launchActivity(intent); //通過name為"word"找到搜尋輸入框 onWebView().withElement(findElement(Locator.NAME, "word")) //往輸入框中輸入字元串"android" .perform(DriverAtoms.webKeys("android")) //通過id為"index-bn"找到"百度一下"按鈕 .withElement(DriverAtoms.findElement(Locator.ID, "index-bn")) //執行點選事件 .perform(webClick()) //通過id為"results"找到結果div .withElement(DriverAtoms.findElement(Locator.ID, "results")) //檢查div中是否包含字元串"android" .check(WebViewAssertions.webMatches(DriverAtoms.getText(), Matchers.containsString("android"))); } }
4 > 測試運作效果
2、Ui Automator測試
Ui Automator自動化測試最顯著的特點就是可以與多個app進行互動。
1 > 首先,在Android Studio 3.0中建立一個項目“UIAutomatorTest”,建立方法參考Android Studio安裝與配置第3項。
同時删除自動生成的一些檔案,最終目錄結構如下:
2> 在app目錄下的build.gradle檔案中添加下面的引入,根據提示點選Sync Now3 > 生成測試類,在MainActivity類中單擊滑鼠右鍵 →Go To →Test →Create New Testdependencies { implementation fileTree(dir: 'libs', include: ['*.jar']) implementation 'com.android.support:appcompat-v7:26.1.0' implementation 'com.android.support.constraint:constraint-layout:1.0.2' implementation 'com.android.support:support-annotations:26.1.0' testImplementation 'junit:junit:4.12' androidTestImplementation 'com.android.support.test:runner:1.0.1' androidTestImplementation 'com.android.support.test.espresso:espresso-core:3.0.1' androidTestCompile 'com.android.support:support-annotations:26.1.0' // UiAutomator Testing androidTestCompile 'com.android.support.test.uiautomator:uiautomator-v18:2.1.1' androidTestCompile 'org.hamcrest:hamcrest-integration:1.3' }
選擇.../app/src/androidTest/java/...
點選OK,然後可以看到在剛剛選擇的目錄中生成了測試類AutomatorTest
4 > 編寫測試類
在AutomatorTest類中編寫測試代碼,為了測試多個app,選擇前面中的EspressoTest應用和手機中的設定app
package com.hebbe.uiautomatortest; import android.content.Context; import android.content.Intent; import android.support.test.InstrumentationRegistry; import android.support.test.filters.SdkSuppress; import android.support.test.runner.AndroidJUnit4; import android.support.test.uiautomator.By; import android.support.test.uiautomator.UiDevice; import android.support.test.uiautomator.UiObject; import android.support.test.uiautomator.UiScrollable; import android.support.test.uiautomator.UiSelector; import android.support.test.uiautomator.Until; import android.widget.Button; import android.widget.EditText; import android.widget.ImageButton; import android.widget.ListView; import android.widget.ScrollView; import android.widget.TextView; import org.junit.Test; import org.junit.runner.RunWith; import static org.hamcrest.Matchers.notNullValue; import static org.junit.Assert.*; /** * Created by Hebbe on 2018/1/17. */ @RunWith(AndroidJUnit4.class) @SdkSuppress(minSdkVersion = 22) public class AutomatorTest { private static final String PACKAGE_ESPRESSOTEST = "com.hebbe.espressotest"; private static final String PACKAGE_SETTING = "com.android.settings"; @Test public void testEspressoTestApp() throws Exception { //初始化一個UiDevice對象 UiDevice mDevice = UiDevice.getInstance(InstrumentationRegistry.getInstrumentation()); // 點選home鍵,回到home界面 mDevice.pressHome(); String launcherPackage = mDevice.getLauncherPackageName(); assertThat(launcherPackage,notNullValue()); mDevice.wait(Until.hasObject(By.pkg(launcherPackage).depth(0)),3); // 啟動espressotest App Context context = InstrumentationRegistry.getContext(); Intent intent = context.getPackageManager().getLaunchIntentForPackage(PACKAGE_ESPRESSOTEST); // 清除以前的執行個體 intent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TASK); context.startActivity(intent); // 等待應用程式啟動 mDevice.wait(Until.hasObject(By.pkg(PACKAGE_ESPRESSOTEST).depth(0)),3); //通過id找到輸入框一 UiObject edt1 = mDevice.findObject(new UiSelector().resourceId("com.hebbe.espressotest:id/editText").className(EditText.class)); //往裡面輸入字元2 edt1.setText("2"); //通過id找到輸入框二 UiObject edt2 = mDevice.findObject(new UiSelector().resourceId("com.hebbe.espressotest:id/editText2").className(EditText.class)); //往裡面輸入字元5 edt2.setText("5"); //通過文本"計算"找到按鈕 UiObject btn = mDevice.findObject(new UiSelector().text("計算").className(Button.class)); //執行點選事件,計算結果 btn.click(); //通過id找到顯示結果的textview UiObject tvResult = mDevice.findObject(new UiSelector().resourceId("com.hebbe.espressotest:id/textView").className(TextView.class)); //判斷結果與預期是否比對 assertEquals(tvResult.getText(),"計算結果:7"); //點選傳回關閉對話框 mDevice.pressBack(); } @Test public void testSettingApp() throws Exception { //初始化一個UiDevice對象 UiDevice mDevice = UiDevice.getInstance(InstrumentationRegistry.getInstrumentation()); Context context = InstrumentationRegistry.getContext(); //回到home界面 mDevice.pressHome(); // 啟動設定 Intent intent = context.getPackageManager().getLaunchIntentForPackage(PACKAGE_SETTING); // 清除以前的執行個體 intent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TASK); context.startActivity(intent); //通過id找到scrollview UiScrollable scrollview = new UiScrollable(new UiSelector().className(ScrollView.class).resourceId("com.android.settings:id/dashboard")); //滑動到底部 scrollview.flingForward(); //通過文本找到關于手機 UiObject aboutPhone = scrollview.getChild(new UiSelector().text("關于手機")); //點選跳轉到手機資訊界面 aboutPhone.click(); //通過description找到向上傳回的ImageButton UiObject ibtnBack = mDevice.findObject(new UiSelector().className(ImageButton.class).description("向上導航")); //點選傳回 ibtnBack.click(); //滑動到包含"提示音和通知"的地方 scrollview.scrollTextIntoView("提示音和通知"); //通過顯示的文本找到控件 UiObject notify = scrollview.getChild(new UiSelector().text("提示音和通知")); //點選跳轉到下一個界面 notify.click(); //通過顯示的文本"手機鈴聲"找到控件 UiObject sound = mDevice.findObject(new UiSelector().text("手機鈴聲")); //點選跳轉到鈴聲對話框 sound.click(); //通過id找到鈴聲清單 UiScrollable listview = new UiScrollable(new UiSelector().className(ListView.class).resourceId("android:id/select_dialog_listview")); //活動到包含"Beat Plucker"處 listview.scrollTextIntoView("Beat Plucker"); //通過顯示的文本找到該項 UiObject beat = listview.getChild(new UiSelector().text("Beat Plucker")); //執行點選選中鈴聲 beat.click(); //通過文本"确定"找到對話框中的确定按鈕 UiObject btnConfirm = mDevice.findObject(new UiSelector().text("确定").className(Button.class)); //點選确定關閉對話框 btnConfirm.click(); //通過id找到顯示結果的TextView UiObject tvSound = mDevice.findObject(new UiSelector().resourceId("android:id/summary").className(TextView.class)); //比較與預期結果是否一緻 assertEquals(tvSound.getText(),"Beat Plucker"); //點選home鍵 mDevice.pressHome(); //點選最近應用鍵 mDevice.pressRecentApps(); //通過類名找到顯示最近app的控件TaskStackView UiScrollable taskStackView = new UiScrollable(new UiSelector().className("com.android.systemui.recents.views.TaskStackView")); //滑動到包含"EspressoTests"處 taskStackView.scrollTextIntoView("EspressoTest"); //通過顯示的文本找到item UiObject espressoTestApp = taskStackView.getChild(new UiSelector().text("EspressoTest")); //點選切換到前面的espressoTestsApp espressoTestApp.click(); } }
5 > 測試運作效果
直接在測試類上單擊滑鼠右鍵 → 選擇 Run 'AutomatorTest'即可。
3、Ui Automator View的使用
從上面的測試代碼可以看到,需要提前知道目标控件的一些屬性值,然後再圍繞目标屬性值建構一個比對規則。
而實際開發中,我們并不知道app的實作,控件的屬性也并不清楚,這時,便可以使用Android提供的uiautormatorviewer工具來分析。(uiautormatorviewer路徑:...\Android\Sdk\tools\bin)
1 > Android Studio中點選“ Tools ” → “Android”→“Android Device Monitor ”,彈出Android Device Monitor 界面。
2 > 該界面最左邊會顯示連接配接的裝置
3 > 點選“Dump View Hierarchy for UI Automator” 圖示,顯示目前頁面布局
4 > 在右邊會顯示布局結構以及view詳細的資訊
參考:
Android自動化測試