初探UiAutomator2.0中使用Xpath定位元素

題外話

最近更新有點(diǎn)延遲哈,那是因?yàn)榻恿艘粋€(gè)外包項(xiàng)目的活(就是移動(dòng)端自動(dòng)化相關(guān)的),忙的“外黑里焦”的城舞,好在應(yīng)該2個(gè)星期的努力已經(jīng)進(jìn)入尾期逛腿,項(xiàng)目整體功能都已經(jīng)實(shí)現(xiàn),后面有空給大家分享沦辙,今天的主題是講一下在使用過(guò)程中遇到的一個(gè)問(wèn)題,如何在UiAutomator2.0中使用Xpath定位元素?

背景

現(xiàn)在的app在打包成apk的時(shí)候都是有加固處理的挡闰,各種混淆加固,所以已經(jīng)破壞了或擾亂了原本的代碼變量命名形式,這就給我們要基于界面來(lái)做自動(dòng)化測(cè)試帶來(lái)了災(zāi)難性的阻礙摄悯,因?yàn)槟切┗煜^(guò)的id是不固定的赞季,下一次再出個(gè)新版本,這一切都變了奢驯,所以這就沒(méi)辦法用id來(lái)定位混淆過(guò)的app元素申钩,那還有什么好的方法嗎?還記得Web自動(dòng)化測(cè)試中神乎其技的xpath嗎瘪阁?不管什么元素都可以用它定位出來(lái)撒遣,所以我就想在UiAutomator2.0中也使用它來(lái)定位混淆的app元素,這要如何操作管跺?UiAutomator2.0的API中并沒(méi)有給出xpath這種方式义黎,那我們只能自己去寫(xiě)一個(gè)了。

思路

參考UI Automator Viewer中抓取到的結(jié)構(gòu)層次豁跑,不能用resource-id轩缤,又要體現(xiàn)出層次關(guān)系,那就只能是class屬性了贩绕,這里的class可以對(duì)應(yīng)web xpath中的標(biāo)簽火的,使用業(yè)界統(tǒng)一的斜杠/來(lái)保持層次,那么最原始狀態(tài)下的xpath大概就是這個(gè)樣子了:

android.view.ViewGroup/android.widget.ImageView
再加上下標(biāo)
android.view.ViewGroup[2]/android.widget.ImageView[0]


xpath的格式定義出來(lái)了之后淑倾,我們就開(kāi)始一層一層去遍歷馏鹤,很簡(jiǎn)單通過(guò)斜杠/來(lái)分隔出一個(gè)class數(shù)組,然后依次去查找這些class對(duì)應(yīng)的元素娇哆,通過(guò)父子關(guān)系拼接起來(lái)湃累,直到最后一個(gè)class,存在就返回對(duì)應(yīng)的對(duì)象碍讨,不存在就返回null治力。
由于時(shí)間關(guān)系,這一次就是初探勃黍,只實(shí)現(xiàn)了這種絕對(duì)路徑(/)下的定位宵统,其實(shí)要想完整完成這個(gè)功能,還需要支持相對(duì)路徑(//)的定位覆获,以及各種屬性的組合定位马澈,其實(shí)基于這個(gè)版本上面改改也不遠(yuǎn)了,這就留給有興趣的童鞋去完成吧弄息。

實(shí)現(xiàn)

1痊班、首先要實(shí)現(xiàn)根據(jù)class或其他屬性去找到某個(gè)元素的子元素,我這里實(shí)現(xiàn)了支持傳入各種參數(shù)摹量,代碼如下:

public static UiObject2 getChild(Object root, Map<String,String> params) {
        if (params == null || !params.containsKey("class")) {
            log.e("[Error]參數(shù)錯(cuò)誤: 為空或未包含[class]key");
            return null;
        }
        String clazz = params.get("class");
        String className = clazz;
        int index = 0;
        if (clazz.endsWith("]") && clazz.contains("[")) { //有下標(biāo)
            className = clazz.substring(0, clazz.lastIndexOf("["));
            String num = clazz.substring(clazz.lastIndexOf("[") + 1, clazz.lastIndexOf("]"));
            index = num != null && !"".equals(num) ? Integer.parseInt(num) : index;
        }
        List<UiObject2> childList = null;
        if (root instanceof UiObject2) {
            childList = ((UiObject2) root).getChildren();
        } else {
            childList = hasObjects(By.clazz(className)) ? mDevice.findObjects(By.clazz(className)) : null;
        }
        List<UiObject2> tempList = new ArrayList<UiObject2>();
        if (childList != null && !childList.isEmpty()) {
            for (UiObject2 child : childList) {
                boolean isMatch = child.getClassName().equals(className);
                if (params.containsKey("pkg")) {
                    isMatch = isMatch && child.getApplicationPackage().equals(params.get("pkg"));
                }

                if (params.containsKey("text")) {
                    isMatch = isMatch && child.getText().equals(params.get("text"));
                }

                if (params.containsKey("desc")) {
                    isMatch = isMatch && child.getContentDescription().equals(params.get("desc"));
                }

                if (isMatch) {
                    tempList.add(child);
                }
            }
        }

        if(tempList.isEmpty()) {
            return null;
        }

        if (index >= tempList.size()) {
            log.e(String.format("[Error]查找class[%s] 下標(biāo)[%d]越界[%d]", clazz, index, tempList.size()));
            return null;
        }
        return tempList.get(index);
    }

2涤伐、再寫(xiě)一個(gè)通過(guò)class獲取子元素的簡(jiǎn)單實(shí)現(xiàn)馒胆,因?yàn)檫@種方式用的多:

  public static UiObject2 getChild(Object root, String clazz) {
        Map<String,String> params = new HashMap<String,String>();
        params.put("class", clazz);
        return getChild(root, params);
    }

3、加入解析xpath表達(dá)式的部分凝果,將解析和查找整個(gè)過(guò)程連起來(lái):

public static UiObject2 findObjectByXpath(UiObject2 root, String xpath) {
        if (xpath == null && "".equals(xpath)) {
            log.e("[Error]xpath expression[" + xpath + "] is invalid");
            return null;
        }
        String[] xpaths = null;
        if (xpath.contains("/")) {
            xpaths = xpath.split("/");
        } else {
            xpaths = new String[]{xpath};
        }
        UiObject2 preNode = root;
        for (String path : xpaths) {
            preNode = getChild(preNode, path);
            if (preNode == null) {
                //log.e(String.format("按xpath[%s]查找元素失敗, 未找到class[%s]對(duì)應(yīng)的節(jié)點(diǎn)", xpath, path));
                break;
            }
        }

        return preNode;
    }

4国章、使用演示:

String commentXpath = "android.widget.LinearLayout/android.widget.LinearLayout/android.widget.TextView[0]";
UiObject2 commentView = findObjectByXpath(root, commentXpath);

總結(jié)

既然是初探就先寫(xiě)這么多吧,給個(gè)實(shí)現(xiàn)思路豆村,如果把整個(gè)功能都完成液兽,可以考慮開(kāi)源到github上方便千千萬(wàn)萬(wàn)其他U2自動(dòng)化的童鞋,后面有時(shí)間可以考慮一下掌动,我更希望有童鞋主動(dòng)來(lái)實(shí)現(xiàn)(哈哈四啰,不做測(cè)試了,沒(méi)以前那么大的熱情和精力來(lái)搞這個(gè)了)粗恢。

原文來(lái)自下方公眾號(hào)柑晒,轉(zhuǎn)載請(qǐng)聯(lián)系作者,并務(wù)必保留出處眷射。
想第一時(shí)間看到更多原創(chuàng)技術(shù)好文和資料匙赞,請(qǐng)關(guān)注公眾號(hào):測(cè)試開(kāi)發(fā)棧

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個(gè)濱河市妖碉,隨后出現(xiàn)的幾起案子涌庭,更是在濱河造成了極大的恐慌,老刑警劉巖欧宜,帶你破解...
    沈念sama閱讀 218,682評(píng)論 6 507
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件坐榆,死亡現(xiàn)場(chǎng)離奇詭異,居然都是意外死亡冗茸,警方通過(guò)查閱死者的電腦和手機(jī)席镀,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,277評(píng)論 3 395
  • 文/潘曉璐 我一進(jìn)店門(mén),熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái)夏漱,“玉大人豪诲,你說(shuō)我怎么就攤上這事」掖拢” “怎么了屎篱?”我有些...
    開(kāi)封第一講書(shū)人閱讀 165,083評(píng)論 0 355
  • 文/不壞的土叔 我叫張陵,是天一觀的道長(zhǎng)扮授。 經(jīng)常有香客問(wèn)我芳室,道長(zhǎng),這世上最難降的妖魔是什么刹勃? 我笑而不...
    開(kāi)封第一講書(shū)人閱讀 58,763評(píng)論 1 295
  • 正文 為了忘掉前任,我火速辦了婚禮嚎尤,結(jié)果婚禮上荔仁,老公的妹妹穿的比我還像新娘。我一直安慰自己,他們只是感情好乏梁,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,785評(píng)論 6 392
  • 文/花漫 我一把揭開(kāi)白布次洼。 她就那樣靜靜地躺著,像睡著了一般遇骑。 火紅的嫁衣襯著肌膚如雪卖毁。 梳的紋絲不亂的頭發(fā)上,一...
    開(kāi)封第一講書(shū)人閱讀 51,624評(píng)論 1 305
  • 那天落萎,我揣著相機(jī)與錄音亥啦,去河邊找鬼。 笑死练链,一個(gè)胖子當(dāng)著我的面吹牛翔脱,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播媒鼓,決...
    沈念sama閱讀 40,358評(píng)論 3 418
  • 文/蒼蘭香墨 我猛地睜開(kāi)眼届吁,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼!你這毒婦竟也來(lái)了绿鸣?” 一聲冷哼從身側(cè)響起疚沐,我...
    開(kāi)封第一講書(shū)人閱讀 39,261評(píng)論 0 276
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤,失蹤者是張志新(化名)和其女友劉穎潮模,沒(méi)想到半個(gè)月后濒旦,有當(dāng)?shù)厝嗽跇?shù)林里發(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 45,722評(píng)論 1 315
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡再登,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,900評(píng)論 3 336
  • 正文 我和宋清朗相戀三年尔邓,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片锉矢。...
    茶點(diǎn)故事閱讀 40,030評(píng)論 1 350
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡梯嗽,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出沽损,到底是詐尸還是另有隱情灯节,我是刑警寧澤,帶...
    沈念sama閱讀 35,737評(píng)論 5 346
  • 正文 年R本政府宣布绵估,位于F島的核電站炎疆,受9級(jí)特大地震影響,放射性物質(zhì)發(fā)生泄漏国裳。R本人自食惡果不足惜形入,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,360評(píng)論 3 330
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望缝左。 院中可真熱鬧亿遂,春花似錦浓若、人聲如沸。這莊子的主人今日做“春日...
    開(kāi)封第一講書(shū)人閱讀 31,941評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)。三九已至耳舅,卻和暖如春碌上,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背浦徊。 一陣腳步聲響...
    開(kāi)封第一講書(shū)人閱讀 33,057評(píng)論 1 270
  • 我被黑心中介騙來(lái)泰國(guó)打工馏予, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人辑畦。 一個(gè)月前我還...
    沈念sama閱讀 48,237評(píng)論 3 371
  • 正文 我出身青樓吗蚌,卻偏偏與公主長(zhǎng)得像,于是被迫代替她去往敵國(guó)和親纯出。 傳聞我的和親對(duì)象是個(gè)殘疾皇子蚯妇,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,976評(píng)論 2 355

推薦閱讀更多精彩內(nèi)容

  • Spring Cloud為開(kāi)發(fā)人員提供了快速構(gòu)建分布式系統(tǒng)中一些常見(jiàn)模式的工具(例如配置管理,服務(wù)發(fā)現(xiàn)暂筝,斷路器箩言,智...
    卡卡羅2017閱讀 134,659評(píng)論 18 139
  • Android 自定義View的各種姿勢(shì)1 Activity的顯示之ViewRootImpl詳解 Activity...
    passiontim閱讀 172,162評(píng)論 25 707
  • 劉蘭蘭 18852657993 年少時(shí),盼著時(shí)光走的快些焕襟,那樣就可以省去許多青澀的片斷陨收,如今則希望時(shí)光止步,...
    高小美_6a97閱讀 202評(píng)論 0 0
  • 不忘初心鸵赖,方得始終务漩。我是一個(gè)軟件技術(shù)從業(yè)者,加入過(guò)不少技術(shù)社群它褪。加入寫(xiě)作社群饵骨,這是第一次,值得記上一筆茫打。 加入00...
    Ashton閱讀 196評(píng)論 1 2
  • 1 2016年7月24日 第一次見(jiàn)面 我去見(jiàn)的他(捂臉)我真的厚顏無(wú)恥 哎呀 那會(huì)應(yīng)該特別期待 不然頂著大太陽(yáng) ...
    銀針一朵閱讀 349評(píng)論 0 0