Uiautomator如何增強腳本的穩(wěn)定性

使用resourceid定位控件

UISelector提供的定位的方式很多,可以是類名,文本,資源id,索引值等,但是索引、文本很容易隨版本變化,類名重復(fù)程度又太高,而資源id通常是不會變的,使用多種條件混合有時效果更好。

執(zhí)行控件方法前判斷是否存在

if (mDevice.hasObject(By.clazz(TextView.class).res(downloadRes))){
    UiObject downloag = mDevice.findObject(new UiSelector().className(TextView.class).resourceId(downloadRes));
    downloag.clickAndWaitForNewWindow(mOutTime/2);
    if (mDevice.hasObject(By.text(lostApk))){
        UiObject confirmBtn = mDevice.findObject(new UiSelector().resourceId(dialogbtnRes));
        confirmBtn.click();
    }
    waitAndInstall();
}

多使用clickAndWaitForNewWindow

對于有跳轉(zhuǎn)的click,使用clickAndWaitForNewWindow會比直接點擊更有效,這個方法等待新窗口的出現(xiàn)后才返回,降低了由于卡頓而導(dǎo)致跳轉(zhuǎn)慢最終找不到控件的概率

查找控件失敗的可能原因

有的控件設(shè)置了屬性NAF=true,這個屬性大概就是no access flag之類的,表示不能被自動化工具識別到,這種控件我一般用起父控件的坐標(biāo)去點擊。
資料:https://stuff.mit.edu/afs/sipb/project/android/docs/tools/testing/testing_ui.html

用一些重試機制使腳本更穩(wěn)定

對于自動化,最關(guān)注的不是功能點擊的實現(xiàn),而是腳本的穩(wěn)定性,兼容性,為了寫把一個click寫好,很可能要額外寫10幾行代碼,下面寫的是如何寫一個兼容性強的apk安裝腳本

protected void waitAndInstall() throws UiObjectNotFoundException{
        if(mDevice.hasObject(By.clazz(Button.class).text("下一步"))){
            UiObject btn = mDevice.findObject(new UiSelector().text("下一步").className(Button.class));
            btn.click();
            waitAndInstall();//循環(huán)查找下一步
        }else if(mDevice.hasObject(By.clazz(Button.class).text("安裝"))){
            UiObject btn = mDevice.findObject(new UiSelector().text("安裝").className(Button.class));
            btn.click();
            btn = mDevice.findObject(new UiSelector().text("確定").className(TextView.class));
            btn.waitForExists(30000);
            btn.click();
        }else{
           
            mDevice.pressBack();//進入到這個流程通常時點擊下載或安裝時彈出了《是否需要root自動安裝》《推薦其他應(yīng)用》的彈窗。這類彈窗沒有規(guī)律
            UiObject btn = mDevice.findObject(new UiSelector().text("下一步").className(Button.class));
            btn.waitForExists(mOutTime/2);
            btn.click();
            waitAndInstall();
        }
    }

使用UIWatcher對異常情況處理,增強穩(wěn)定性

腳本運行時的異常彈窗,谷歌當(dāng)然也會預(yù)料到,所以在UiAutomator里提供了UiWatcher這個接口,希望腳本編寫者能夠在異常時進行一些處理。
UiWatcher的使用簡單,首先它是一個接口,其次它只有一個方法需要實現(xiàn),下面是其接口定義。

public interface UiWatcher {

    /**
     * Custom handler that is automatically called when the testing framework is unable to
     * find a match using the {@link UiSelector}
     *
     * When the framework is in the process of matching a {@link UiSelector} and it
     * is unable to match any widget based on the specified criteria in the selector,
     * the framework will perform retries for a predetermined time, waiting for the display
     * to update and show the desired widget. While the framework is in this state, it will call
     * registered watchers' checkForCondition(). This gives the registered watchers a chance
     * to take a look at the display and see if there is a recognized condition that can be
     * handled and in doing so allowing the current test to continue.
     *
     * An example usage would be to look for dialogs popped due to other background
     * processes requesting user attention and have nothing to do with the application
     * currently under test.
     *
     * @return true to indicate a matched condition or false for nothing was matched
     * @since API Level 16
     */
    public boolean checkForCondition();
}

從接口的注釋可以看到,當(dāng)我們注冊了watcher時,如果通過selector沒有找到我們想要的Ui元素,就會調(diào)用watcher。具體使用方法如下,首先實現(xiàn)這個接口,在我的安裝自動化中,安裝完apk后經(jīng)常有些app彈窗問是否要刪除安裝包,影響腳本后續(xù)的點擊。所以我寫了這個watcher,當(dāng)觸發(fā)時,如果UI中找到了類似這個彈窗,那么我點擊系統(tǒng)back按鍵取消這個彈窗,使我的腳本繼續(xù)執(zhí)行。

public class MyWatcher implements UiWatcher {
    private UiDevice mDevice;
    public MyWatcher(UiDevice device){
        mDevice = device;
    }
    @Override
    public boolean checkForCondition() {

        if(mDevice.hasObject(By.text("刪除安裝包"))){
            mDevice.pressBack();

            return true;
        }
        return false;
    }
}

完成定以后,在腳本的setUp里注冊自己的watcher,當(dāng)控件查找失敗時就會自動調(diào)用watcher了。

myWatcher = new MyWatcher(mDevice);
mDevice.registerWatcher("testwatcher",myWatcher);

下面我們看下watcher是如何增強腳本穩(wěn)定性的。以下是UiObject中查找控件的方法,可以看到當(dāng)查找控件失敗時,就會調(diào)用device的runWatcher方法啟動所有注冊的watcher,然后如果沒有超時就會再次尋找。所以如果我們在watcher里把彈窗處理掉,那么下次查找就會成功了。

protected AccessibilityNodeInfo findAccessibilityNodeInfo(long timeout) {
        AccessibilityNodeInfo node = null;
        long startMills = SystemClock.uptimeMillis();
        long currentMills = 0;
        while (currentMills <= timeout) {
            node = getQueryController().findAccessibilityNodeInfo(mUiSelector);
            if (node != null) {
                break;
            } else {
                // does nothing if we're reentering another runWatchers()
                mDevice.runWatchers();
            }
            currentMills = SystemClock.uptimeMillis() - startMills;
            if(timeout > 0) {
                SystemClock.sleep(WAIT_FOR_SELECTOR_POLL);
            }
        }
        return node;
    }

實際用的過程中,不管是調(diào)用device的findObject還是hasObject,如果查找失敗都會調(diào)用到watcher,所以watcher里一定要根據(jù)實際狀態(tài)進行處理,切不可統(tǒng)一做處理。

takeScreenShot失???

這兩天寫自動化時有時會截圖失敗,會提示device or resource is busy,后來發(fā)現(xiàn)是RootExplorer打開著沒有完全退出,只是退到了后臺,應(yīng)該是RE打開時把文件系統(tǒng)重洗掛載了導(dǎo)致無法寫入截圖文件。
同時在實踐也發(fā)現(xiàn)takescreenshot函數(shù)的重載版,設(shè)置圖片質(zhì)量和縮放時貌似是無效的,如果圖片有傳輸要求還是自己寫代碼壓縮吧

即使你了解了這些技巧,就目前來看,仍不建議去做功能自動化,腳本的穩(wěn)定性保證需要很多額外的代碼,提升卻很有限

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容