Macaca基礎(chǔ)原理淺析

導(dǎo)語(yǔ)

前面幾篇文章介紹了在Macaca實(shí)踐中的一些實(shí)用技巧與解決方案晌缘,今天簡(jiǎn)單分析一下Macaca的基礎(chǔ)原理。這篇文章將以前面所分享的UI自動(dòng)化Macaca-Java版實(shí)踐心得中的demo為基礎(chǔ)敦锌,進(jìn)行一下實(shí)例講解兔沃。尚不了解Macaca為何物的同學(xué)請(qǐng)移步 Macaca -Java版入門指南

Macaca的基本組成

通過對(duì)源碼各個(gè)模塊的分析玛迄,可以幫助我們對(duì)Macaca的整體構(gòu)成有一個(gè)基礎(chǔ)的認(rèn)識(shí)。Macaca已經(jīng)開源宁舰,相關(guān)的源碼在對(duì)應(yīng)的github上都可以下載:

https://github.com/macacajs

大家會(huì)在阿里巴巴集團(tuán)的開源github上找到macaca的另一個(gè)倉(cāng)庫(kù)https://github.com/alibaba/macaca/,關(guān)于這兩個(gè)倉(cāng)庫(kù)的關(guān)系這里簡(jiǎn)單講一下拼卵,由于主倉(cāng)庫(kù)https://github.com/macacajs模塊眾多,所以在alibaba集團(tuán)下的github上采用了alibaba/macaca,以方便管理蛮艰,大家如果需要源碼的話需要去macaca的主倉(cāng)庫(kù)查看间学,也就是https://github.com/macacajs

為了方便大家查看印荔,寶寶費(fèi)了好大勁畫了下面這張圖(覺得不錯(cuò)的給個(gè)贊吧):

macaca_structure.png

備注:上圖所有模塊均可以在官方github上找到對(duì)應(yīng)的源碼 https://github.com/macacajs

模塊拆分講解:

Macaca

1. macaca-cli

Macaca提供的命令行工具

$macaca server 啟動(dòng)server

$macaca server --verbose 啟動(dòng)server并打印詳細(xì)日志

$macaca doctor 檢驗(yàn)當(dāng)前macaca環(huán)境配置

2. app-inspector

macaca提供的元素查找工具低葫,可以將app視圖的結(jié)構(gòu)以布局結(jié)構(gòu)樹的格式在瀏覽器上展示出來,用過點(diǎn)擊某個(gè)元素仍律,就可以方便的查詢到該控件的基本信息嘿悬,以方便查找。具體使用可參考官網(wǎng): https://macacajs.com/inspector

3. UI Recorder

macaca提供的腳本錄制工具水泉,可以通過錄制獲得腳本善涨,對(duì)于入門同學(xué)很有幫助蒂萎。https://macacajs.com/recorder

WebDriver-Server

Macaca是按照經(jīng)典的Server-Client設(shè)計(jì)模式進(jìn)行設(shè)計(jì)的让禀,也就是我們常說的C/S架構(gòu)。WebDriver-server部分便充當(dāng)了server這部分的角色最蕾,他的職責(zé)就是等待client發(fā)送請(qǐng)求并做出響應(yīng)炕横。

WebDriver-Client

client端簡(jiǎn)單來講就是我們的測(cè)試代碼源内,我們測(cè)試代碼中的一些行為,比如控件查找份殿、點(diǎn)擊等膜钓,這些行為以http請(qǐng)求的方式發(fā)送給server,server接收請(qǐng)求嗽交,并執(zhí)行相應(yīng)操作,并在response中返回執(zhí)行狀態(tài)颂斜、返回值等信息夫壁。

也正是基于這種經(jīng)典的C/S架構(gòu),所以client端具有跨語(yǔ)言的特點(diǎn)沃疮,macaca-wd盒让,wd.java,wd.py分別是Macaca團(tuán)隊(duì)針對(duì)Js Java 以及Python的封裝,只要能保證client端遵循webdriver的協(xié)議,任意語(yǔ)言都可以司蔬。

DriverList

自動(dòng)化要在不同的平臺(tái)上跑糯彬,需要有對(duì)應(yīng)平臺(tái)的驅(qū)動(dòng),這部分驅(qū)動(dòng)接收到來自server的操作命令葱她,驅(qū)動(dòng)各自平臺(tái)的底層完成對(duì)應(yīng)的操作。

1. Android

Macaca針對(duì)安卓平臺(tái)的驅(qū)動(dòng)集合

  • macaca-android 安卓驅(qū)動(dòng)
  • macaca-adb 封裝了安卓的adb命令似扔,來實(shí)現(xiàn)一些adb的操作吨些,比如安裝、卸載炒辉、啟動(dòng)app豪墅、獲取設(shè)備列表這些操作
  • android-unicode 經(jīng)過封裝后的輸入法,解決中文輸入的問題
  • uiautomator-client 將來自server的操作指令轉(zhuǎn)換為UIAutomator可以識(shí)別的指令黔寇,驅(qū)動(dòng)uiautomator完成對(duì)應(yīng)的操作
  • android-performance 用于自動(dòng)化測(cè)試安卓性能相關(guān)的支持
2. iOS

Macaca針對(duì)iOS平臺(tái)的驅(qū)動(dòng)集合

  • macaca-ios iOS驅(qū)動(dòng)
  • xctest-client 同安卓的uiautomator-client異曲同工偶器,對(duì)XCUITest的封裝,將來自server的操作指令轉(zhuǎn)換為XCUITest可以識(shí)別的指令缝裤,驅(qū)動(dòng)XCUITest完成對(duì)應(yīng)的操作
  • ios-simulator 用于對(duì)ios模擬器的支持屏轰,可以通過模擬器運(yùn)行用例
  • remote-debug 用于遠(yuǎn)程調(diào)試
3. Hybrid

Macaca針對(duì)Hybrid的驅(qū)動(dòng)集合。

  • macaca-chrome web測(cè)試驅(qū)動(dòng)
  • macaca-chromedriver 驅(qū)動(dòng)chrome瀏覽器
  • ios-webkit-debug-proxy 適用于iOS平臺(tái)對(duì)webview的調(diào)試
4. Electron

Macaca針對(duì)pc端網(wǎng)頁(yè)應(yīng)用的支持

  • macaca-electron

Macaca執(zhí)行流程圖

了解了Macaca的組成模塊以及他們各自的作用憋飞,下面我們看一下各個(gè)模塊是如何組裝起來實(shí)現(xiàn)自動(dòng)化測(cè)試流程的霎苗,寶寶同樣費(fèi)了很大勁畫了一張圖如下:

macaca_flow.png

結(jié)合實(shí)例講解Macaca基本原理:

以文章開始提到的demo為例(client以Java版為例) demo地址

源碼克隆到本地并配置好Macaca相關(guān)環(huán)境后,我們來執(zhí)行一次用例:

1. 啟動(dòng)macaca server

?  bootstrap git:(master) ? macaca server --verbose
>> request.js:24:12 [master] pid:5499 get remote update info failed.
>> index.js:17:12 [master] pid:5503 webdriver server start with config:
 { port: 3456,
  verbose: true,
  always: true,
  ip: '30.30.180.23',
  host: 'MacBook-Pro.local',
  loaded_time: '2016-12-07 17:00:22' }
>> middlewares.js:17:10 [master] pid:5503 base middlewares attached
>> router.js:129:10 [master] pid:5503 router set
>> webdriver sdk launched

從這一步打印的信息我們可以看到榛做,這一步實(shí)際上執(zhí)行的是流程圖中第一步的操作唁盏,啟動(dòng)server,建立連接检眯,然后server返回所連接的ip以及端口號(hào)厘擂,因?yàn)槲覀兪潜镜嘏埽詉p實(shí)際上是本機(jī)的ip地址

2. 執(zhí)行用例

以SampleTest為例锰瘸,右鍵執(zhí)行junitTest刽严,稍作等待,就會(huì)看到系統(tǒng)自動(dòng)啟動(dòng)了ios的模擬器并跑起來了用例避凝。執(zhí)行過程中的某個(gè)截圖如下:

image

首先我們來看一下對(duì)應(yīng)用例啟動(dòng)的client端核心代碼:


    @Before
    public void setUp() throws Exception {

        // 清除日志記錄
        ResultGenerator.clearOldData();
        //清理截圖重新記錄
        File file = new File(Config.ScreenshotPath);
        deleteOldScreen(file);

        // 初始化應(yīng)用基礎(chǔ)信息
        JSONObject props = new JSONObject();
        if (Config.PLATFORM.equals("ios")) {

            // 創(chuàng)建ios實(shí)例
            props.put("app", Config.IOS_APP);
            props.put("platformName", Config.IOS_PLATFORM_NAME);
            props.put("deviceName", Config.IOS_DEVICE_NAME);
            driver.setCurPlatform(PlatformType.IOS);
        } else {

            //創(chuàng)建安卓實(shí)例
            props.put("app", Config.ADR_APP);
            props.put("platformName", Config.ADR_PLATFORM_NAME);
            driver.setCurPlatform(PlatformType.ANDROID);
        }

        // 覆蓋安裝
        props.put("reuse", Config.REUSE);

        JSONObject desiredCapabilities = new JSONObject();
        desiredCapabilities.put("desiredCapabilities", props);
        driver.initDriver(desiredCapabilities);

    }

在這段代碼中港庄,我們做的工作是根據(jù)不同的平臺(tái)設(shè)置用例的一些基礎(chǔ)啟動(dòng)信息倔既,包含平臺(tái)類型、安裝包地址鹏氧、設(shè)備id渤涌、是否覆蓋安裝等參數(shù),設(shè)置完成后把还,通過driver.initDriver(desiredCapabilities)這個(gè)操作啟動(dòng)driver,這個(gè)過程便會(huì)按照流程圖中的第二個(gè)步驟發(fā)送http請(qǐng)求实蓬,server會(huì)接收到這個(gè)請(qǐng)求并創(chuàng)建一個(gè)session,在這次的用例執(zhí)行中吊履,所有的操作都會(huì)基于這個(gè)session進(jìn)行安皱,來看一下針對(duì)這個(gè)操作控制臺(tái)所打印的信息(為方便突出主要過程省略了部分無關(guān)日志):

>> responseHandler.js:11:12 [master] pid:5503 Recieve HTTP Request from Client: method: POST url: /wd/hub/session, jsonBody: {"desiredCapabilities":{"app":"/Users/Macaca/github/bootstrap/app/ios-app-bootstrap.zip","reuse":"3","platformName":"iOS","deviceName":"iPhone 6"}}
>> session.js:47:10 [master] pid:5503 Creating session, sessionId: abe8f19c-76ea-4bb0-b5b9-d69e3ce9b798.
>> helper.js:196:12 [master] pid:5503 Unzipping local app form /Users/Macaca/github/bootstrap/app/ios-app-bootstrap.zip
>> macaca-ios.js:194:10 [master] pid:5503 Get available devices(...省略設(shè)備列表)
...省略部分信息
>> proxy.js:54:14 [master] pid:5503 Proxy: /session:POST to http://30.30.180.23:8900/session:POST with body: {"desiredCapabilities":{"bundleId":"xudafeng.ios-app-bootstrap","app":"/var/folders/lf/lmrfrj9s4xn76wq_4k3x92380000gn/T/ios-app-bootstrap.app/","platformName":"iOS"}}
>> proxy.js:67:16 [master] pid:5503 Got response with status 200: {"value":{"sessionId":"6A1D2ED3-37BD-449C-A128-2E72DEF4CBF9","capabilities":{"device":"iphone","browserName":"ios-app-bootstrap","sdkVersion":"10.1","CFBundleIdentifier":"xudafeng.ios-app-bootstrap...
>> responseHandler.js:47:14 [master] pid:5503 Send HTTP Respone to Client: {"sessionId":"abe8f19c-76ea-4bb0-b5b9-d69e3ce9b798","status":0,"value":"{\"app\":\"/var/folders/lf/lmrfrj9s4xn76wq_4k3x92380000gn/T/ios-app-bootstrap.app/\",\"reuse\":\"3\",\"platformName\":\"iOS\",\"deviceName\":\"iPhone 6\"}"}

經(jīng)過如上步驟后,連接便已經(jīng)成功建立了艇炎,下一步再分析一下一個(gè)具體操作酌伊,以登錄為例,對(duì)應(yīng)的client端的代碼如下:

(因?yàn)榭蚣軐臃庋b了一些操作缀踪,所以代碼看上去比較少居砖,具體的控件查找部分看不到,有需要詳細(xì)了解的可以研究源碼)


// SampleTest.java

    @Test
    public void test () throws Exception {

        // 處理登錄
        LoginPage loginPage = new LoginPage("登錄頁(yè)");
        loginPage.setDriver(driver);
        if (loginPage.hasPageShown(LoginPageUI.LOGIN_BTN)) {
            saveScreen(loginPage.pageDesc);
            ResultGenerator.loadPageSucc(loginPage);
            loginPage.login("test", "123");
        } else {
            ResultGenerator.loadPageFail(loginPage);

        }
    }

對(duì)應(yīng)登錄按鈕的查詢操作驴娃,我們會(huì)在控制臺(tái)上看到如下的日志:

>> responseHandler.js:11:12 [master] pid:5503 Recieve HTTP Request from Client: method: POST url: /wd/hub/session/abe8f19c-76ea-4bb0-b5b9-d69e3ce9b798/element, jsonBody: {"using":"name","value":"Login"}
>> proxy.js:54:14 [master] pid:5503 Proxy: /wd/hub/session/abe8f19c-76ea-4bb0-b5b9-d69e3ce9b798/element:POST to http://30.30.180.23:8900/session/6A1D2ED3-37BD-449C-A128-2E72DEF4CBF9/element:POST with body: {"using":"name","value":"Login"}
>> proxy.js:67:16 [master] pid:5503 Got response with status 200: {"value":{"using":"name","value":"Login","description":"unable to find an element"},"sessionId":"abe8f19c-76ea-4bb0-b5b9-d69e3ce9b798","status":7}
>> session.js:107:14 [master] pid:5503 Send HTTP Respone to Client: {"value":"{\"using\":\"name\",\"value\":\"Login\",\"description\":\"unable to find an element\"}","sessionId":"abe8f19c-76ea-4bb0-b5b9-d69e3ce9b798","status":7}

在上面的日志中我們可以看到奏候,當(dāng)我們查找登錄按鈕的時(shí)候,client發(fā)送了一個(gè)http請(qǐng)求給server唇敞,請(qǐng)求的操作是element(這個(gè)表示控件查找),參數(shù)是{"using":"name","value":"Login"}蔗草,這是告訴server我們要找的這個(gè)控件的name屬性是Login,server收到這個(gè)請(qǐng)求疆柔,通過router路由轉(zhuǎn)發(fā)給iOS的驅(qū)動(dòng)(在啟動(dòng)driver的時(shí)候已經(jīng)設(shè)置的平臺(tái)類型咒精,因此這里能知道找ios),iOS驅(qū)動(dòng)收到請(qǐng)求驅(qū)動(dòng)XCUITest框架對(duì)模擬器上的目標(biāo)app執(zhí)行對(duì)應(yīng)的控件查找操作旷档,得到response后原路返回給client狠轻,這樣就完成了一次請(qǐng)求的完整的生命周期。

一點(diǎn)題外話

關(guān)于Macaca使用中的問題彬犯,很多同學(xué)會(huì)直接去社區(qū)里提問向楼,但是很多時(shí)候大家問的問題都是類似的,這種情況建議大家先去查看一下官方倉(cāng)庫(kù)的issues谐区,看看有沒有人遇到自己同樣問題的湖蜕,如果沒有,可以新建issue宋列。查看issue之前先看下自己?jiǎn)栴}所屬的模塊昭抒,比如如果問題在wd.java的使用中,可以去wd.java倉(cāng)庫(kù)下查看issue:https://github.com/macacajs/wd.java/issues

小結(jié)

如上簡(jiǎn)單總結(jié)了Macaca的基礎(chǔ)原理,提供一個(gè)小竅門是大家可以對(duì)照控制臺(tái)輸出與文章中的流程圖一一對(duì)應(yīng)灭返,這樣就能大體了解整個(gè)流程的數(shù)據(jù)流向盗迟,從而就能參透Macaca的基礎(chǔ)原理了。個(gè)人水平有限熙含,如有不當(dāng)罚缕,歡迎指正。
后面會(huì)陸續(xù)放出自己在實(shí)踐中的其他心得與經(jīng)驗(yàn)怎静,敬請(qǐng)期待邮弹,也歡迎大家交流討論。

附錄

“Macaca開源社區(qū)”群的釘釘群號(hào): 11775486(歡迎入群討論)

最后編輯于
?著作權(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)離奇詭異塘装,居然都是意外死亡急迂,警方通過查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,277評(píng)論 3 395
  • 文/潘曉璐 我一進(jìn)店門氢哮,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人型檀,你說我怎么就攤上這事冗尤。” “怎么了胀溺?”我有些...
    開封第一講書人閱讀 165,083評(píng)論 0 355
  • 文/不壞的土叔 我叫張陵裂七,是天一觀的道長(zhǎng)。 經(jīng)常有香客問我仓坞,道長(zhǎng)背零,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 58,763評(píng)論 1 295
  • 正文 為了忘掉前任无埃,我火速辦了婚禮徙瓶,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘嫉称。我一直安慰自己侦镇,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,785評(píng)論 6 392
  • 文/花漫 我一把揭開白布织阅。 她就那樣靜靜地躺著壳繁,像睡著了一般。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上闹炉,一...
    開封第一講書人閱讀 51,624評(píng)論 1 305
  • 那天蒿赢,我揣著相機(jī)與錄音,去河邊找鬼渣触。 笑死羡棵,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的昵观。 我是一名探鬼主播晾腔,決...
    沈念sama閱讀 40,358評(píng)論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼,長(zhǎng)吁一口氣:“原來是場(chǎng)噩夢(mèng)啊……” “哼啊犬!你這毒婦竟也來了灼擂?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 39,261評(píng)論 0 276
  • 序言:老撾萬榮一對(duì)情侶失蹤觉至,失蹤者是張志新(化名)和其女友劉穎剔应,沒想到半個(gè)月后,有當(dāng)?shù)厝嗽跇淞掷锇l(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
  • 文/蒙蒙 一悠汽、第九天 我趴在偏房一處隱蔽的房頂上張望箱吕。 院中可真熱鬧,春花似錦柿冲、人聲如沸殖氏。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,941評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)雅采。三九已至爵憎,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間婚瓜,已是汗流浹背宝鼓。 一陣腳步聲響...
    開封第一講書人閱讀 33,057評(píng)論 1 270
  • 我被黑心中介騙來泰國(guó)打工, 沒想到剛下飛機(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)容