http://www.jianshu.com/p/7718860ec657 2016.07.25 20:59 字數 3675 Android UIAutomator淺談 -------------------------------------------------------------------------------- 簡介 Uiautomator是谷歌推出的,用於UI自動化測試的工具,也就是普通的手工測試,點擊每一個控件元素看看輸出的結果是否符合預期。好比登錄界面分別輸入正確和錯誤的用戶名密碼而後點擊登錄按鈕看看是否可否登錄以及是否有錯誤提示等。 注意:UI Automator測試框架是基於instrumentation的API,運行在Android JunitRunner 之上,同時UI Automator Test只運行在Android 4.3(API level 18)以上版本。 準備 集成UI Automator,首先須要保證App項目已經依賴了Gradle Testing。而後在gradle中添加以下依賴便可。 dependencies { androidTestCompile 'com.android.support.test.uiautomator:uiautomator-v18:2.1.1' } UI元素查找 在Android開發中,能夠種findViewById來找到相應的控件,可是UI Automator是用來測試界面的,若是也是用相同的方式,那麼則會依賴很大。UI Automator使用一種相似的方式來查找元素。UI Automator是根據元素的text、hint、contentDescription等屬性進行查找的,爲了使UI Automator可以獲取到以上的屬性值,請測試的UI界面是能夠訪問的,同時UI Automator也是能夠訪問這些控件元素。 可使用uiautomatorviewer,來獲取界面元素的層次關係以及各個元素的屬性,而後UI Automator就能夠根據這些屬性信息查找到對應的控件元素。 獲取步驟: 啓動設備上的App 把設備鏈接到開發機器上 打開Android sdk目錄,<android-sdk>/tools/。 啓動uiautomatorviewer 而後能夠操做uiautomatorviewer 點擊Device ScreenShot,稍等一會,獲取到了屏幕的快照,界面右側是當前界面的佈局結構以及屬性信息。 確保UI能夠訪問 上面介紹過,UI Automator是根據元素的text、hint、contentDescription等屬性進行查找的,因此須要儘量給佈局中的元素添加上面的屬性。有時候程序員須要自定義一些控件,那麼請實現AccessibilityNodeProvider,以確保能夠正常使用。 訪問UI控件 UI Automator 提供 UiDevice 類,這個類提供一些方法,能夠獲取設備的一些狀態和屬性,同時能夠執行一系列動做來操做設備。下面是一個示例,演示怎麼樣貨到 UiDevice 對象,以及按下Home鍵。 import org.junit.Before; import android.support.test.runner.AndroidJUnit4; import android.support.test.uiautomator.UiDevice; import android.support.test.uiautomator.By; import android.support.test.uiautomator.Until; ... @RunWith(AndroidJUnit4.class) @SdkSuppress(minSdkVersion = 18) public class ChangeTextBehaviorTest { private static final String BASIC_SAMPLE_PACKAGE = "com.example.android.testing.uiautomator.BasicSample"; private static final int LAUNCH_TIMEOUT = 5000; private static final String STRING_TO_BE_TYPED = "UiAutomator"; private UiDevice mDevice; @Before public void startMainActivityFromHomeScreen() { // Initialize UiDevice instance mDevice = UiDevice.getInstance(InstrumentationRegistry.getInstrumentation()); // Start from the home screen mDevice.pressHome(); // Wait for launcher final String launcherPackage = mDevice.getLauncherPackageName(); assertThat(launcherPackage, notNullValue()); mDevice.wait(Until.hasObject(By.pkg(launcherPackage).depth(0)), LAUNCH_TIMEOUT); // Launch the app Context context = InstrumentationRegistry.getContext(); final Intent intent = context.getPackageManager() .getLaunchIntentForPackage(BASIC_SAMPLE_PACKAGE); // Clear out any previous instances intent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TASK); context.startActivity(intent); // Wait for the app to appear mDevice.wait(Until.hasObject(By.pkg(BASIC_SAMPLE_PACKAGE).depth(0)), LAUNCH_TIMEOUT); } }而後可使用findObject()方法來獲取到界面的UI控件,以下所示,在一個設備上面找出符合規則的UI元素,而後再執行一些列動做。 UiObject cancelButton = mDevice.findObject(new UiSelector() .text("Cancel")) .className("android.widget.Button")); UiObject okButton = mDevice.findObject(new UiSelector() .text("OK")) .className("android.widget.Button")); // Simulate a user-click on the OK button, if found. if(okButton.exists() && okButton.isEnabled()) { okButton.click(); }指定一個選擇器 有時候,須要訪問一個特定的UI組件,可使用UiSelector。這個類提供一些列的方法,幫助找到特定的UI組件。 當有多個符合條件UI元素被查找到時候,第一個元素會不是使用。能夠傳遞的一個UiSelector到構造方法,至關於鏈式訪問。若是沒有元素被找到,那麼會拋出UiAutomatorObjectNotFoundException異常。 也可使用childSelector()來帥選多個UiSelector對象,以下所示,ListView的子元素中有不少是相同的,能夠根據這個方法,選擇某一個子元素。 UiObject appItem = new UiObject(new UiSelector() .className("android.widget.ListView") .instance(1) .childSelector(new UiSelector() .text("Apps")));上面的查找仍是很複雜,咱們能夠根據ResourceId 來替代文本查找的方式。文本找到的方式很脆弱,並且很容使測試失敗,並且當語言環境變化了,文本查找就須要對照多個翻譯版本的文本了。 執行動做 當咱們根據文本查找找到對應的文本元素後,咱們就能夠爲所欲爲的進行操做了。 click() 點擊 dragTo() 拖動當前元素 setText() 設置文本 swipeUp() 向上滑動,同理也有向下、向左、向右,swipeDown、swipeLeft、swipeRight。 UI Automator也能夠提供一個Context,這樣能夠方便發送一個Intent或者是啓動一個Activity。 public void setUp() { // Launch a simple calculator app Context context = getInstrumentation().getContext(); Intent intent = context.getPackageManager() .getLaunchIntentForPackage(CALC_PACKAGE); intent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TASK); // Clear out any previous instances context.startActivity(intent); mDevice.wait(Until.hasObject(By.pkg(CALC_PACKAGE).depth(0)), TIMEOUT); }集合操做 可使用UiCollection來模擬用戶對一組UI控件進行操做,好比有時候界面上有個Listview。爲了建立一個 UiCollection 對象,能夠指定一個UiSelector搜索這個UI容器上知足這個條件的全部子元素。 下面代碼演示如何在一個一組控件中進行操做。 UiCollection videos = new UiCollection(new UiSelector() .className("android.widget.FrameLayout")); // Retrieve the number of videos in this collection: int count = videos.getChildCount(new UiSelector() .className("android.widget.LinearLayout")); // Find a specific video and simulate a user-click on it UiObject video = videos.getChildByText(new UiSelector() .className("android.widget.LinearLayout"), "Cute Baby Laughing"); video.click(); // Simulate selecting a checkbox that is associated with the video UiObject checkBox = video.getChild(new UiSelector() .className("android.widget.Checkbox")); if(!checkBox.isSelected()) checkbox.click();操做能夠滾動的UI元素 可使用UiScrollable來模擬用戶對界面的滾動操做(水平、垂直),這個技術能夠幫咱們搞定,當某個界面沒有顯示的時候,咱們能夠滾動界面,把那個界面顯示出來。 UiScrollable settingsItem = new UiScrollable(new UiSelector() .className("android.widget.ListView")); UiObject about = settingsItem.getChildByText(new UiSelector() .className("android.widget.LinearLayout"), "About tablet"); about.click();校驗結果 Ui Automator 是基於InstrumentationTestCase,一樣InstrumentationTestCase是基於標準的JUnit Assert,那麼咱們也可使用標準的JUnit Assert來判斷結果。 下面代碼片斷演示一個計算器的代碼,以及驗證結果。 private static final String CALC_PACKAGE = "com.myexample.calc"; public void testTwoPlusThreeEqualsFive() { // Enter an equation: 2 + 3 = ? mDevice.findObject(new UiSelector() .packageName(CALC_PACKAGE).resourceId("two")).click(); mDevice.findObject(new UiSelector() .packageName(CALC_PACKAGE).resourceId("plus")).click(); mDevice.findObject(new UiSelector() .packageName(CALC_PACKAGE).resourceId("three")).click(); mDevice.findObject(new UiSelector() .packageName(CALC_PACKAGE).resourceId("equals")).click(); // Verify the result = 5 UiObject result = mDevice.findObject(By.res(CALC_PACKAGE, "result")); assertEquals("5", result.getText()); }Api UiDevice void clearLastTraversedText() // Clears the text from the last UI traversal event. // 清楚上次UI遍歷的事件? boolean click(int x, int y) // Perform a click at arbitrary coordinates specified by the user // 根據座標點擊 boolean drag(int startX, int startY, int endX, int endY, int steps) // Performs a swipe from one coordinate to another coordinate. // 拖動 void dumpWindowHierarchy(File dest) // Dump the current window hierarchy to a File. // dump當前的層次化結構到文件中 void dumpWindowHierarchy(OutputStream out) // Dump the current window hierarchy to an OutputStream. // dump當前的層次化結構到流中 void dumpWindowHierarchy(String fileName) // This method is deprecated. Use dumpWindowHierarchy(File) or dumpWindowHierarchy(OutputStream) instead. // dump當前的層次化結構到文件中 UiObject2 findObject(BySelector selector) // Returns the first object to match the selector criteria. // 根據BySelector查找 UiObject findObject(UiSelector selector) // Returns a UiObject which represents a view that matches the specified selector criteria. // 根據UiSelector 查找 List<UiObject2> findObjects(BySelector selector) // Returns all objects that match the selector criteria. // 根據BySelector查找 void freezeRotation() // Disables the sensors and freezes the device rotation at its current rotation state. // 凍結旋轉的狀態 String getCurrentActivityName() // This method is deprecated. The results returned should be considered unreliable // 獲取當前Activity的名字,已經被廢棄 String getCurrentPackageName() // Retrieves the name of the last package to report accessibility events. // 獲取當前package int getDisplayHeight() // Gets the height of the display, in pixels. int getDisplayRotation() // Returns the current rotation of the display, as defined in Surface Point getDisplaySizeDp() // Returns the display size in dp (device-independent pixel) The returned display size is adjusted per screen rotation. int getDisplayWidth() // Gets the width of the display, in pixels. static UiDevice getInstance() // This method is deprecated. Should use getInstance(Instrumentation) instead. This version hides UiDevice's dependency on having an Instrumentation reference and is prone to misuse. // 獲取一個對象 static UiDevice getInstance(Instrumentation instrumentation) // Retrieves a singleton instance of UiDevice String getLastTraversedText() // Retrieves the text from the last UI traversal event received. // 獲取上一次遍歷的文本 String getLauncherPackageName() // Retrieves default launcher package name // 獲取運行的packagename String getProductName() // Retrieves the product name of the device. boolean hasAnyWatcherTriggered() // Checks if any registered UiWatcher have triggered. // 檢查是否有觸發器觸發 boolean hasObject(BySelector selector) // Returns whether there is a match for the given selector criteria. // 是否有符合的條件的 boolean hasWatcherTriggered(String watcherName) // Checks if a specific registered UiWatcher has triggered. boolean isNaturalOrientation() // Check if the device is in its natural orientation. boolean isScreenOn() // Checks the power manager if the screen is ON. boolean openNotification() // Opens the notification shade. // 打開通知 boolean openQuickSettings() // Opens the Quick Settings shade. // 打開設置 <R> R performActionAndWait(Runnable action, EventCondition<R> condition, long timeout) // Performs the provided action and waits for the condition to be met. boolean pressBack() // Simulates a short press on the BACK button. boolean pressDPadCenter() // Simulates a short press on the CENTER button. boolean pressDPadDown() // Simulates a short press on the DOWN button. boolean pressDPadLeft() // Simulates a short press on the LEFT button. boolean pressDPadRight() // Simulates a short press on the RIGHT button. boolean pressDPadUp() // Simulates a short press on the UP button. boolean pressDelete() // Simulates a short press on the DELETE key. boolean pressEnter() // Simulates a short press on the ENTER key. boolean pressHome() // Simulates a short press on the HOME button. boolean pressKeyCode(int keyCode) // Simulates a short press using a key code. boolean pressKeyCode(int keyCode, int metaState) // Simulates a short press using a key code. boolean pressMenu() // Simulates a short press on the MENU button. boolean pressRecentApps() // Simulates a short press on the Recent Apps button. boolean pressSearch() // Simulates a short press on the SEARCH button. void registerWatcher(String name, UiWatcher watcher) // Registers a UiWatcher to run automatically when the testing framework is unable to find a match using a UiSelector. void removeWatcher(String name) // Removes a previously registered UiWatcher. void resetWatcherTriggers() // Resets a UiWatcher that has been triggered. void runWatchers() // This method forces all registered watchers to run. void setCompressedLayoutHeirarchy(boolean compressed) // Enables or disables layout hierarchy compression. void setOrientationLeft() // Simulates orienting the device to the left and also freezes rotation by disabling the sensors. // 設置旋轉方向 void setOrientationNatural() // Simulates orienting the device into its natural orientation and also freezes rotation by disabling the sensors. void setOrientationRight() // Simulates orienting the device to the right and also freezes rotation by disabling the sensors. void sleep() // This method simply presses the power button if the screen is ON else it does nothing if the screen is already OFF. // 關閉屏幕 boolean swipe(int startX, int startY, int endX, int endY, int steps) // Performs a swipe from one coordinate to another using the number of steps to determine smoothness and speed. boolean swipe(Point[] segments, int segmentSteps) // Performs a swipe between points in the Point array. boolean takeScreenshot(File storePath, float scale, int quality) // Take a screenshot of current window and store it as PNG The screenshot is adjusted per screen rotation // 截屏 boolean takeScreenshot(File storePath) // Take a screenshot of current window and store it as PNG Default scale of 1.0f (original size) and 90% quality is used The screenshot is adjusted per screen rotation void unfreezeRotation() // Re-enables the sensors and un-freezes the device rotation allowing its contents to rotate with the device physical rotation. <R> R wait(SearchCondition<R> condition, long timeout) // Waits for given the condition to be met. void waitForIdle(long timeout) // Waits for the current application to idle. void waitForIdle() // Waits for the current application to idle. boolean waitForWindowUpdate(String packageName, long timeout) // Waits for a window content update event to occur. void wakeUp() // This method simulates pressing the power button if the screen is OFF else it does nothing if the screen is already ON. // 點亮屏幕UiObject void clearTextField() // Clears the existing text contents in an editable field. // 清空輸入接口 boolean click() // Performs a click at the center of the visible bounds of the UI element represented by this UiObject. // 點擊 boolean clickAndWaitForNewWindow() // Waits for window transitions that would typically take longer than the usual default timeouts. // 點擊並等待新界面 boolean clickAndWaitForNewWindow(long timeout) // Performs a click at the center of the visible bounds of the UI element represented by this UiObject and waits for window transitions. // 點擊並等待新界面,設置等待時間 boolean clickBottomRight() // Clicks the bottom and right corner of the UI element // 點擊右下邊 boolean clickTopLeft() // Clicks the top and left corner of the UI element boolean dragTo(UiObject destObj, int steps) // Drags this object to a destination UiObject. // 拖動 boolean dragTo(int destX, int destY, int steps) // Drags this object to arbitrary coordinates. boolean exists() // Check if view exists. // 判斷是否存在 Rect getBounds() // Returns the view's bounds property. // 返回邊界 UiObject getChild(UiSelector selector) // Creates a new UiObject for a child view that is under the present UiObject. // 根據條件獲取子元素 int getChildCount() // Counts the child views immediately under the present UiObject. // 獲取子元素數量 String getClassName() // Retrieves the className property of the UI element. // 獲取當前元素的class name String getContentDescription() // Reads the content_desc property of the UI element UiObject getFromParent(UiSelector selector) // Creates a new UiObject for a sibling view or a child of the sibling view, relative to the present UiObject. String getPackageName() // Reads the view's package property final UiSelector getSelector() // Debugging helper. String getText() // Reads the text property of the UI element Rect getVisibleBounds() // Returns the visible bounds of the view. // 獲取可見邊界 boolean isCheckable() // Checks if the UI element's checkable property is currently true. // 是否能夠點擊 boolean isChecked() // Check if the UI element's checked property is currently true // 是否已經選中 boolean isClickable() // Checks if the UI element's clickable property is currently true. boolean isEnabled() // Checks if the UI element's enabled property is currently true. boolean isFocusable() // Check if the UI element's focusable property is currently true. boolean isFocused() // Check if the UI element's focused property is currently true boolean isLongClickable() // Check if the view's long-clickable property is currently true boolean isScrollable() // Check if the view's scrollable property is currently true boolean isSelected() // Checks if the UI element's selected property is currently true. boolean longClick() // Long clicks the center of the visible bounds of the UI element // 長按 boolean longClickBottomRight() // Long clicks bottom and right corner of the UI element boolean longClickTopLeft() // Long clicks on the top and left corner of the UI element boolean performMultiPointerGesture(PointerCoords... touches) // Performs a multi-touch gesture. boolean performTwoPointerGesture(Point startPoint1, Point startPoint2, Point endPoint1, Point endPoint2, int steps) // Generates a two-pointer gesture with arbitrary starting and ending points. boolean pinchIn(int percent, int steps) // Performs a two-pointer gesture, where each pointer moves diagonally toward the other, from the edges to the center of this UiObject . boolean pinchOut(int percent, int steps) // Performs a two-pointer gesture, where each pointer moves diagonally opposite across the other, from the center out towards the edges of the this UiObject. boolean setText(String text) // Sets the text in an editable field, after clearing the field's content. // 設置輸入內容 boolean swipeDown(int steps) // Performs the swipe down action on the UiObject. boolean swipeLeft(int steps) // Performs the swipe left action on the UiObject. boolean swipeRight(int steps) // Performs the swipe right action on the UiObject. boolean swipeUp(int steps) // Performs the swipe up action on the UiObject. boolean waitForExists(long timeout) // Waits a specified length of time for a view to become visible. boolean waitUntilGone(long timeout) // Waits a specified length of time for a view to become undetectable.總結 優勢: 能夠對全部操做進行自動化,操做簡單 不須要對被測程序進行重簽名,且,能夠測試全部設備上的程序,好比~某APP,好比~撥號,好比~發信息等等 對於控件定位,要比robotium簡單一點點 缺點: Ui Automator須要android level 16以上纔可使用,由於在level 16及以上的API裏面才帶有uiautomator工具 若是想要使用resource-id定位控件,則須要level 18及以上才能夠 對中文支持很差(不表明不支持,第三方jar能夠實現)