Macaca Macaca 基础原理浅析

Yinxl · December 07, 2016 · Last by findarice replied at December 24, 2018 · 6458 hits
本帖已被设为精华帖!

导语

前面几篇文章介绍了在Macaca实践中的一些实用技巧与解决方案,今天简单分析一下Macaca的基础原理。这篇文章将以前面所分享的UI自动化Macaca-Java版实践心得中的demo为基础,进行一下实例讲解。

Macaca的基本组成

通过对源码各个模块的分析,可以帮助我们对Macaca的整体构成有一个基础的认识。Macaca已经开源,相关的源码在对应的github上都可以下载:

https://github.com/macacajs

大家会在alibaba集团的开源github上找到macaca的另一个仓库https://github.com/alibaba/macaca/,关于这两个仓库的关系这里简单讲一下,由于主仓库https://github.com/macacajs模块众多,所以在alibaba集团下的github上采用了alibaba/macaca,以方便管理,大家如果需要源码的话需要去macaca的主仓库查看,也就是https://github.com/macacajs

为了方便大家查看,宝宝费了好大劲画了下面这张图(觉得不错的给个赞吧):

structure

备注:上图所有模块均可以在官方github上找到对应的源码 https://github.com/macacajs

模块拆分讲解:

Macaca

1. macaca-cli

Macaca提供的命令行工具

$macaca server 启动server

$macaca server --verbose 启动server并打印详细日志

$macaca doctor 检验当前macaca环境配置

2. app-inspector

macaca提供的元素查找工具,可以将app视图的结构以布局结构树的格式在浏览器上展示出来,用过点击某个元素,就可以方便的查询到该控件的基本信息,以方便查找。具体使用可参考官网: https://macacajs.com/inspector

3. UI Recorder

macaca提供的脚本录制工具,可以通过录制获得脚本,对于入门同学很有帮助。https://macacajs.com/recorder

WebDriver-Server

Macaca是按照经典的Server-Client设计模式进行设计的,也就是我们常说的C/S架构。WebDriver-server部分便充当了server这部分的角色,他的职责就是等待client发送请求并做出响应。

WebDriver-Client

client端简单来讲就是我们的测试代码,我们测试代码中的一些行为,比如控件查找、点击等,这些行为以http请求的方式发送给server,server接收请求,并执行相应操作,并在response中返回执行状态、返回值等信息。

也正是基于这种经典的C/S架构,所以client端具有跨语言的特点,macaca-wd,wd.java,wd.py分别是Macaca团队针对Js Java 以及Python的封装,只要能保证client端按照指定的要求发送Http请求,任意语言都可以。

DriverList

自动化要在不同的平台上跑,需要有对应平台的驱动,这部分驱动接收到来自server的操作命令,驱动各自平台的底层完成对应的操作。

1. Android

Macaca针对安卓平台的驱动集合

  • macaca-android 安卓驱动
  • macaca-adb 封装了安卓的adb命令,来实现一些adb的操作,比如安装、卸载、启动app、获取设备列表这些操作
  • android-unicode 经过封装后的输入法,解决中文输入的问题
  • uiautomator-client 将来自server的操作指令转换为UIAutomator可以识别的指令,驱动uiautomator完成对应的操作
  • android-performance 用于自动化测试安卓性能相关的支持
2. iOS

Macaca针对iOS平台的驱动集合

  • macaca-ios iOS驱动
  • xctest-client 同安卓的uiautomator-client异曲同工,对XCUITest的封装,将来自server的操作指令转换为XCUITest可以识别的指令,驱动XCUITest完成对应的操作
  • ios-simulator 用于对ios模拟器的支持,可以通过模拟器运行用例
  • remote-debug 用于远程调试
3. Hybrid

Macaca针对Hybrid的驱动集合。

  • macaca-chrome web测试驱动
  • macaca-chromedriver 驱动chrome浏览器
  • ios-webkit-debug-proxy 适用于iOS平台对webview的调试
4. Electron

Macaca针对pc端网页应用的支持

  • macaca-electron

Macaca执行流程图

了解了Macaca的组成模块以及他们各自的作用,下面我们看一下各个模块是如何组装起来实现自动化测试流程的,宝宝同样费了很大劲画了一张图如下:

flow

结合实例讲解Macaca基本原理:

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

源码克隆到本地并配置好Macaca相关环境后,我们来执行一次用例:

1. 启动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

从这一步打印的信息我们可以看到,这一步实际上执行的是流程图中第一步的操作,启动server,建立连接,然后server返回所连接的ip以及端口号,因为我们是本地跑,所以ip实际上是本机的ip地址

2. 执行用例

以SampleTest为例,右键执行junitTest,稍作等待,就会看到系统自动启动了ios的模拟器并跑起来了用例。执行过程中的某个截图如下:

image

首先我们来看一下对应用例启动的client端核心代码:


@Before
public void setUp() throws Exception {

// 清除日志记录
ResultGenerator.clearOldData();
//清理截图重新记录
File file = new File(Config.ScreenshotPath);
deleteOldScreen(file);

// 初始化应用基础信息
JSONObject props = new JSONObject();
if (Config.PLATFORM.equals("ios")) {

// 创建ios实例
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 {

//创建安卓实例
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);

}

在这段代码中,我们做的工作是根据不同的平台设置用例的一些基础启动信息,包含平台类型、安装包地址、设备id、是否覆盖安装等参数,设置完成后,通过driver.initDriver(desiredCapabilities)这个操作启动driver,这个过程便会按照流程图中的第二个步骤发送http请求,server会接收到这个请求并创建一个session,在这次的用例执行中,所有的操作都会基于这个session进行,来看一下针对这个操作控制台所打印的信息(为方便突出主要过程省略了部分无关日志):

>> 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(...省略设备列表)
...省略部分信息
>> 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\"}"}

经过如上步骤后,连接便已经成功建立了,下一步再分析一下一个具体操作,以登录为例,对应的client端的代码如下:

(因为框架层封装了一些操作,所以代码看上去比较少,具体的控件查找部分看不到,有需要详细了解的可以研究源码)


// SampleTest.java

@Test
public void test () throws Exception {

// 处理登录
LoginPage loginPage = new LoginPage("登录页");
loginPage.setDriver(driver);
if (loginPage.hasPageShown(LoginPageUI.LOGIN_BTN)) {
saveScreen(loginPage.pageDesc);
ResultGenerator.loadPageSucc(loginPage);
loginPage.login("test", "123");
} else {
ResultGenerator.loadPageFail(loginPage);

}
}

对应登录按钮的查询操作,我们会在控制台上看到如下的日志:

>> 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}

在上面的日志中我们可以看到,当我们查找登录按钮的时候,client发送了一个http请求给server,请求的操作是element(这个表示控件查找),参数是{"using":"name","value":"Login"},这是告诉server我们要找的这个控件的name属性是Login,server收到这个请求,通过router路由转发给iOS的驱动(在启动driver的时候已经设置的平台类型,因此这里能知道找ios),iOS驱动收到请求驱动XCUITest框架对模拟器上的目标app执行对应的控件查找操作,得到response后原路返回给client,这样就完成了一次请求的完整的生命周期。

一点题外话

关于Macaca使用中的问题,很多同学会直接去社区里提问,但是很多时候大家问的问题都是类似的,这种情况建议大家先去查看一下官方仓库的issues,看看有没有人遇到自己同样问题的,如果没有,可以新建issue。查看issue之前先看下自己问题所属的模块,比如如果问题在wd.java的使用中,可以去wd.java仓库下查看issue:https://github.com/macacajs/wd.java/issues

小结

如上简单总结了Macaca的基础原理,提供一个小窍门是大家可以对照控制台输出与文章中的流程图一一对应,这样就能大体了解整个流程的数据流向,从而就能参透Macaca的基础原理了。个人水平有限,如有不当,欢迎指正。
后面会陆续放出自己在实践中的其他心得与经验,敬请期待,也欢迎大家交流讨论。

附录

“Macaca开源社区”群的钉钉群号: 11775486(欢迎入群讨论,钉钉顶部搜索框搜索群号加入即可)

更多相关文章

UI 自动化框架调研总结
从无到有搭建 Macaca 环境 (forMac)
Macaca-Java 版入门指南
UI 自动化 Macaca-Java 版实践心得
UI 自动化利器-为你的应用自动添加控件 ID 探索
Macaca 基础原理浅析

如果觉得我的文章对您有用,请随意打赏。您的支持将鼓励我继续创作!
共收到 37 条回复 时间 点赞
恒温 将本帖设为了精华贴 07 Dec 19:35

图画的很漂亮

#2楼 @Lihuazhang 哈哈 值了 谢谢!

好文,谢谢分享

非常细致,清晰明了

想问一下有没有关于脚本是如何写的这样的教程,用node.js的,一个脚本是如何完成的,已macaca-mobile-sample.test.js这个脚本为例,还有关于inspector的使用,另外一篇关于inspector的使用的文档太简单了,inspector启动后具体该如何查找控件,这些内容有什么比较详细的文档,或者网课可以看,看到现在完全没有一个思路,一个测试脚本是如何产生的,只知道环境搭一下,然后去看node.js的语言,然后再去看脚本,但是脚本里面的内容又看不懂,
比如说这个,我知道这个是测试登录是否成功的内容,但是为什么这样写,中间的内容如果更换了其他的APP需要做些什么变动修改,想知道这些东西,问题貌似有点多········

感谢分享,图我可以借鉴一下么?

#6楼 @876374178 你是要用Js写用例吗?还是Java? 关于Java的可以参考一下我前面文章中给出的一个demo,有一些注释,看起来应该能比较清楚的。 inspector的使用我随后发一篇文章出来吧

#7楼 @testly 拿走不谢 哈哈哈哈

#8楼 @junhe 是的需要用js写用例·······

图用什么工具画的,眼前一亮

Yinxl #12 · December 08, 2016 作者

#11楼 @Tank007 哈哈 用Mac自带的keynote 哈哈

思路非常清晰,图也画得漂亮👍 👍 👍

支持阿里的这种分享精神,非常棒!💪 👍 👍 👍

请问 iOS 的 remote-debug 这个东西怎么使用?楼主能解释一下吗?

Yinxl #16 · December 10, 2016 作者

#15楼 @AllocAndInit 这个是封装在Macaca框架里面的,对于写自动化用例来说是透明的哈,我们基本用不到的

#6楼 @876374178 兄弟,给你这个链接,我和你的疑问一样,我总结的经验是多看日志,注意关键字,熟练使用百度/谷歌。这是我找到的结果,macaca用js编写用例会用到的mocha框架,里面解释了descibe和it的意思等等。http://www.ruanyifeng.com/blog/2015/12/a-mocha-tutorial-of-examples.html☺,希望对你有用

Yinxl #18 · December 12, 2016 作者

#17楼 @apple863 👍 很详细的回复 赞!

#17楼 @apple863 /(ㄒoㄒ)/~~ ·········心好累···你给的链接打开是404的提示

#17楼 @apple863 没事了·······直接点链接的时候把后面的emoji图片也带进去了,删了就好了+_+

#17楼 @apple863 灰常感谢····也谢谢君禾大美女 感恩

macaca越来越火了哈,macaca与appium相比有哪些明显的优势呢?有点重复造轮子的感觉

#8楼 @junhe 想请教下,inspector装好了,命令行启动后,按照官方文档写的localhost:3456端口,可惜一直看不到内容是咋回事呢,多谢

Yinxl #24 · December 14, 2016 作者

#22楼 @happystone 我有一篇文章就是讲的这个问题哦:https://testerhome.com/topics/6602

Yinxl #25 · December 14, 2016 作者

#23楼 @melody902 localhost:3456这个是默认的 不用自己指定的 你说的看不到内容是指app-inspector看不到内容吗? 最好附带一下截图或者日志哈 没太明白你的意思 嘿嘿

牛逼

原理解释得很清楚

IOS真机上感觉运行速度很慢,不知道为什么

请问楼主,在Macaca 内部执行过程当中,是否是一个 请求对应一系列的执行动作,还是一个请求只对应一个执行动作?
当我 自启动一个 Macaca Server 的时候,然后模拟着像这个 Server 发送满足条件的请求,但是永远都只有终端的响应输出结果,而且请求都是 OK 的,但是真机设备上没有任何的执行动作,请问这是为什么了?

Yinxl #31 · December 19, 2016 作者

一个请求对应一个动作还是一系列动作要看不同请求不同分析的,如果终端响应并输出结果了但是真机上没有执行对应的动作首先要看当前连接的是不是真机这台机器(有没有可能启动了模拟器但是你没看到),另外可以透过终端的输出看一下当前的动作是什么,再对应着看客户端的反映

终于有架构图了 赞 期待再解释下跟appium的差别 以及为什么速度快

请问用例应该怎么组织呢,目前提供的一些demo都很简单,用在项目中的话,不知道怎么组织用例

#34楼 @junhe 谢谢👍 ,大神的用例组织太经典,得好好学学。

Yinxl Macaca-Java 版入门指南 中提及了此贴 28 Jun 20:08
Yinxl UI 自动化 Macaca-Java 版实践心得 中提及了此贴 28 Jun 20:08
Yinxl UI 自动化框架调研总结 中提及了此贴 28 Jun 20:09
Yinxl 从无到有搭建 Macaca 环境 (forMac) 中提及了此贴 28 Jun 20:10
xdf macaca 的一些问题请教 中提及了此贴 12 Aug 16:04
Yinxl Macaca 基础原理浅析 中提及了此贴 29 Oct 14:05

求助,pc桌面版基于electronjs的桌面版应用的UI页面中,需要使用到鼠标右键的操作,试问鼠标右键的模拟操作macaca如何实现呢,api中未发现,借问求分享哦

macaca java 如何切换 windowHandles,我通过webDriver.contexts.getWindowHandle() 这种方式,获取不到handle

请教下,服务器与Android真机之间是怎么连接的?

子非鱼 行业流行测试框架对比 中提及了此贴 18 Mar 21:59
需要 Sign In 后方可回复, 如果你还没有账号请点击这里 Sign Up