Macaca Macaca-Java 版入门指南

Yinxl · 2016年11月21日 · 最后由 joweywen 回复于 2018年10月29日 · 254 次阅读
本帖已被设为精华帖!

以下内容转载自我的云栖社区:https://yq.aliyun.com/users/1035573748035711

导语

接触 Macaca 已经有一段时间,从开始的对于 UI 自动化的调研开始,将 Macaca 与 Appium,Robotium 等自动化方案进行了多方对比,最终 Macaca 脱颖而出成为团队的敲定方案,随后经历了 Macaca 从 JS 版本到 Java 版本的迭代,踩了很多坑,积累了很多经验教训,在这里总结沉淀一下,以方便后来人少走弯路,加快脚步。

一、认识 Macaca


Macaca 是一套基于 WebDriver 标准协议开发的开源自动化解决方案,旨在解决跨平台,跨终端的 UI 自动化测试的短板问题,减少繁杂、重复的人工工作,降低自动化测试的上手门坎。终端上,Macaca 同时支持 Native(iOS&&Android)、Hybrid、Mobile Web、以及 PC 端的自动化测试,用例编写上,Macaca 采取 CS 架构,可以支持任意编程语言的封装,目前已支持 Javascript、Java、Python 三种语言,感兴趣的同学甚至可以自行封装支持任意自己习惯的语言。目前,Macaca 在 testerHome 上的关注度以及活跃度都保持着强劲的势头,他不只提供了基础的用例编写功能,更提供了性能数据,包含电量、内存等指标的采集功能的附带模块,以及一套分布式持续集成系统供有需者选择,选择 Macaca 作为 UI 自动化的框架,将为大家提供很大的便利性。

二、配置 Macaca 开发环境


这部分主要针对 Macaca1.0 版本环境配置进行介绍,Macaca 更新 2.0 后,环境配置上有些许改动,配置最新版 Macaca 环境,请略过本张姐,参考我的最新文章 从无到有搭建 Macaca 环境 (forMac)

三、编写 Macaca-Java 版测试用例


1.下载官方提供的 Java 版 Demo 用例

https://github.com/macaca-sample/sample-java
官方 demo 已更新,如下描述为针对早前版本的描述,仅供参考。

2.Demo 用例分析:

通过 Eclipse 导入 Maven 工程,会发现工程结构相当简单,只有一个 SampleTest.java 类,之所以如此简洁,是因为 Demo 工程将 Macaca 的 Java 源码库以 Maven 源的形式配置到了 pom.xml 中:

<dependency>
        <groupId>macaca.webdriver.client</groupId>
        <artifactId>macacaclient</artifactId>
        <version>1.0.1</version>
</dependency>

如上图,可知道当 Macaca 有更新时,可以通过修改上图中的 version 版本号来实现库的更新。

对 SampleTest.java 源码的分析:

*setUp() 方法主要用于配置被测应用的基础信息,包含平台版本,系统版本,安装包路径等,Demo 实例中提供的为一个 PC 端的实例,当配置移动端应用测试时,相关参数稍有不同,后面会给出对应移动端的 setUp() 配置

@Before
public void setUp() throws Exception {
   // 在setUp()中配置被测应用的基础信息,如平台版本,安装包地址等
    Logger logger = Logger.getLogger(getClass());
    JSONObject porps = new JSONObject();
    porps.put("autoAcceptAlerts", true);
    porps.put("browserName", "electron");
    porps.put("platformName", "desktop");
    porps.put("version", "");
    porps.put("javascriptEnabled", true);
    porps.put("platform", "ANY");
    JSONObject desiredCapabilities = new JSONObject();
    desiredCapabilities.put("desiredCapabilities", porps);
    driver.initDriver(desiredCapabilities).setWindowSize(1280, 800).get("https://www.baidu.com");
}

* 移动端的 setUp() 配置信息实例

       @Before
   public void setUp() throws Exception {

       JSONObject porps = new JSONObject();
       porps.put("autoAcceptAlerts", true);
       porps.put("platformVersion", "9.3");
       porps.put("deviceName", "iPhone 5s");
       porps.put("platformName", "iOS");
       // 指定待测应用的安装包
       porps.put("app", "**/**/targetApp.zip"); 
      // 指定app复用类型,比如需要每次用例启动重装app,可以设置为1,不重装,可以设为3
porps.put("reuse",1);
       JSONObject desiredCapabilities = new JSONObject();
       desiredCapabilities.put("desiredCapabilities", porps);
       driver.initDriver(desiredCapabilities);
       driver.platform = "ios";


   }

针对如上代码,需要注意的是,对于 iOS 平台,在模拟器上跑用例时 app 的安装包需要基于.app 包压缩后的 zip 包 (这个.app 包可以找对应的 iOS 开发同学提供,在 XCode 工程目录下 Products 目录下),而不能用.ipa 包进行压缩,对于真机,可以直接使用.ipa 文件,不过需要涉及证书签名等问题,关于这个,可参考https://testerhome.com/topics/6503
另外,对于配置参数,iOS 与安卓有各自特有的参数,具体配置信息可参考:
https://macacajs.com/helpful-settings
滑动到此页面底部可看到 Desired Capabilities

* 测试用例编写


@Test
public void test_case_1() throws Exception {
    driver
        .elementById("kw")
        .sendKeys("macaca")
        .sleep(1000)
        .elementById("su")
        .click()
        .sleep(3000);

    String html = driver.source();

    Assert.assertThat(html, containsString("<html>"));

    driver
        .elementByCss("#head > div.head_wrapper")
        .elementByXPath("//*[@id=\"kw\"]")
        .sendKeys(" elementByXPath")
        .elementById("su")
        .click()
        .takeScreenshot();
}

可能习惯 Java 的同学对于测试用例的这种链式写法有些不习惯,个人认为这样写与 Javascript 版本的迁移有所关联,优势在于可以便利的实现连续的 UI 操作,而不用一行行写重复样式的调用,而实现这种语法的关键在于源码中每一个接口,比如 elementById(),sendKeys() 都返回了 driver 本身,如下:

    #MacacaClient.java
    /**
 * Search for an element on the page, starting from the document root.
 * @param elementId The ID attribute of element
 * @return The currently instance of MacacaClient
 * @throws Exception
 */
public MacacaClient elementById(String elementId) throws Exception {
    JSONObject jsonObject = new JSONObject();
    jsonObject.put("value", elementId);
    jsonObject.put("using", "id");
    element.findElement(jsonObject);
    return this;
}

以上源码来自于 Java 版 Source Code:https://github.com/macacajs/wd.java

3.Demo 用例运行

同 Javascript 版用例不同的一点是,Javascript 版本的用例可以直接通过一条命令启动 macaca server 并运行 case,而 Java 版的用例则需要单独启动 Macaca server,然后执行测试用例,具体操作如下:

1)进入工程目录
$cd path/to/macaca-test-sample-java
2)启动 Macaca server
$macaca server --verbose

如果想查看启动中的详细信息 可以追加--verbose 参数 $macaca server --verbose

3) 新建 cmd 窗口,进入当前工程目录下:
$mvn -s settings.xml clean install -Dmaven.test.skip=true

注:mvn -s 的作用在于使 maven 以工程目录下的 settings.xml 文件为依据下载依赖,但是实践中发现部分同学会出现 mvn -s 无法生效的作用,这样会导致依赖下载失败,这种情况下,需要大家将工程目录下的 settings.xml 中的配置相应的添加到本地 settings 中。

$mvn test 

或者可以直接进入到 IDE 中运行 test 用例

这样就可以看到自动执行的测试效果了

四、UI 控件查找工具-Inspector


实际应用中自动化用例的编写很多情况下都是查找控件并操作,于是如何快速高效的查找控件成为了一件关乎效率的大问题,最早的时候对于安卓,查找控件依赖于 Android 自带的 UIAutomator viewer 工具,iOS 则依赖于 XCode 中的 Accessibility Inspector 工具,但是这两种工具查找起来相对麻烦,效率相对低下,针对这个问题,Macaca 团队提供了统一的 Inspector 工具,可以以 Web 的方式方便的查看不论是安卓还是 iOS 的控件。

具体使用链接如下:https://macacajs.com/inspector

关于控件的查找另外补充一点,目前 Java 版提供通过 id,css,name,xpath 等方式获取控件,但 xpath 的方式扩展性太弱,不到万不得已不推荐使用,最好的方式是希望对每个控件能有一个唯一性的 id 标示获取,但这需要开发同学的配合,对于安卓,有一些控件是有 id 的,因为开发同学自身也需要定位这个控件,但对于 iOS,有很多控件是不存在 id 的,但是如果有一种底层的方案,按照一定的规则给控件自动增加 id 属性,则只需要在底层做一定适配而不需要开发同学额外给每个控件增加 id 属性,这对于提高自动化的效率大有裨益,在网上找到了一篇类似思想的文章,仅供参考:为 UIAutomation 添加自动化测试标签的探索

常用命令集锦

  1. 启动 server :

    //--verbose为可选参数,用于查看详细日志,可不加
    $macaca server --verbose
    
  2. 查看当前设备 ID:

    Android:

    $adb devices
    

    iOS:

    $xcrun simctl list
    
  3. 查看视图 Dom 树

    //***部分为要监控的设备id
    $ app-inspector -u *** 
    

    前提是要安装了 app-inspector ,安装命令:

    cnpm i app-inspector -g 
    

    参考资料

1.macaca 官方教程

官方教程包含环境搭建/API 参考(Js、Java、Python)/CI 集成等最权威的指南,参考首选。

2.TesterHome 上的 Macaca 社区

testerHome 上包含很多实际应用的帖子,很多步骤介绍的比较详细并配有截图,以及暴露了很多开发者使用过程中的坑,对于扫盲非常有帮助

3.Macaca 源码

想深入了解 Macaca 原理的同学可以研究 Macaca 的源码,Macaca 团队将每个模块单独建立了代码库,看起来清楚明了。如果在开发中遇到了框架层面的问题,也可以在对应的 github 上提 issue,甚至可以提交 PR 参与到 Macaca 的共建中,作为开源框架,Macaca 需要大家共同努力发扬光大。

附录

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

更多相关文章

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

如果觉得我的文章对您有用,请随意打赏。您的支持将鼓励我继续创作!
共收到 33 条回复 时间 点赞

不错,加油

新手入门指南 good

恒温 将本帖设为了精华贴 11月21日 13:51

花了很多心血,赞一个👍

Yinxl UI 自动化 Macaca-Java 版实践心得 中提及了此贴 11月24日 12:32

请问 maven 找不到这个怎么办

楼主,改了版本号还是不行,请问如何解决

#7 楼 @cece0417 抱歉,昨天太忙今天刚看到,你把工程目录下的 settings.xml 里面的配置加到本地 maven 目录 conf 下的 settings.xml 中试一下,执行更新的时候 mvn -s 命令可能没生效

志雄 [该话题已被删除] 中提及了此贴 12月06日 09:28

安装 npm i macaca-android -g 报错,试过换 JDK1.8 也是报错,有人知道这是什么原因吗?

Yinxl #33 · 2016年12月08日 Author

#10 楼 @xiaocong168 看一下你的 Node 版本呢?

Yinxl #13 · 2016年12月08日 Author

#12 楼 @xiaocong168 有可能是 android sdk 没有装全,macaca doctor 贴一下呢

#13 楼 @junhe 好了 就是缺少 ant

好文,楼主踩过很多坑,赞

—— 来自 TesterHome 官方 安卓客户端

17楼 已删除
18楼 已删除

楼主,在测试过程中有遇到过 Error: chromedriver proxy error with: Error: connect ECONNREFUSED 127.0.0.1:9515
这种问题吗?请问怎么解决?

Yinxl #20 · 2017年03月22日 Author
very 回复

需要更详细的 log,这个太简略了,看不出来哇


macaca 能否像 selenium 中的注解来查找元素呢?

Yinxl #23 · 2017年04月06日 Author
loneyao 回复

已更新,但是用例已经跟最开始的有所不同,后面的描述仅供参考,可能会跟新的用例有所出入。

Yinxl #24 · 2017年04月06日 Author
very 回复

这个目前没有考虑,业务实践方面可以参考我的https://testerhome.com/topics/6457

好的,多谢

xcode 8.2.1 mac 系统 10.11.6 运行用例时发现 xctestwd 工程编译失败了,升级到 xcode 到 8.3 也存在这个个问题
/usr/local/lib/node_modules/macaca-ios/node_modules/._xctestwd@1.0.15@xctestwd/XCTestWD/XCTestWDUITests/server/controllers/XCTestWDStatusController.swift:10:8: Module compiled with Swift 3.1 cannot be imported in Swift 3.0.2: /usr/local/lib/node_modules/macaca-ios/node_modules/._xctestwd@1.0.15@xctestwd/XCTestWD/../Carthage/Build/iOS/Swifter.framework/Modules/Swifter.swiftmodule/x86_64.swiftmodule

为何 driver.drag(100f,200f,700f,200f,1000f,1) 这个拖动报错了:java.lang.Exception: Argument was an invalid selector (e.g. XPath/CSS).@xdf

xdf 回复

为何 driver.drag(100f,200f,700f,200f,1000f,1) 这个拖动报错了:java.lang.Exception: Argument was an invalid selector (e.g. XPath/CSS).@xdf

钉钉号失效了吗?有没有一起讨论的地方?谢谢

Yinxl #31 · 2017年06月05日 Author
oneHappiness 回复

钉钉号没有失效,请在首页顶部的搜索框里搜索群号哈,是能搜到的 (我觉得你可能是用了右上角添加好友那里的入口..钉钉这个设计有点儿问题)

Yinxl 从无到有搭建 Macaca 环境 (forMac) 中提及了此贴 06月27日 10:04
Yinxl Macaca 基础原理浅析 中提及了此贴 06月28日 12:07
Yinxl UI 自动化框架调研总结 中提及了此贴 06月28日 12:09

哈喽 请教一下 我本地项目运行找到元素后进行 click() 或者 getText() 都报错:An unknown server-side error occurred while processing the command. 然后 git 了最新的 java 版和 nodejs 版的 demo,也有这个问题。
下面是我运行 demo 的报错 log:

UIAutomatorWD http server ready
proxy.js:55:14 [master] pid:964 Proxy: /wd/hub/session/8beda65d-7ba5-4a93-9bec-8ae74277dc7c/element:POST to http://127.0.0.1:9001/wd/hub/session/8beda65d-7ba5-4a93-9bec-8ae74277dc7c/element:POST with body: {"using":"id","value":"com.github.android_app_bootstrap:id/mobileNoEditText"}
proxy.js:85:20 [master] pid:964 Got response with status 200: {"status":0,"value":{"ELEMENT":"1"},"sessionId":null}
session.js:109:14 [master] pid:964 Send HTTP Respone to Client[2017-11-24 18:00:10]: {"status":0,"value":"{\"ELEMENT\":\"1\"}","sessionId":null}
responseHandler.js:11:12 [master] pid:964 Recieve HTTP Request from Client[2017-11-24 18:00:10]: method: POST url: /wd/hub/session/8beda65d-7ba5-4a93-9bec-8ae74277dc7c/element/1/value, jsonBody: {"value":["中文 +Test+12345678"]}
restart UIAutomatorWD server
uiautomator-client.js:61:14 [master] pid:964 INSTRUMENTATION_RESULT: shortMsg=Process crashed.
uiautomator-client.js:61:14 [master] pid:964
INSTRUMENTATION_CODE: 0

uiautomator-client.js:61:14 [master] pid:964 INSTRUMENTATION_STATUS: numtests=1

uiautomator-client.js:61:14 [master] pid:964 INSTRUMENTATION_STATUS: stream=
com.macaca.android.testing.UIAutomatorWD:
INSTRUMENTATION_STATUS: id=AndroidJUnitRunner
INSTRUMENTATION_STATUS: test=MacacaTestRunner
INSTRUMENTATION_STATUS: class=com.macaca.android.testing.UIAutomatorWD
INSTRUMENTATION_STATUS: current=1
INSTRUMENTATION_STATUS_CODE: 1

uiautomator-client.js:61:14 [master] pid:964 INSTRUMENTATION_STATUS: stream=
uiautomator-client.js:61:14 [master] pid:964

uiautomator-client.js:61:14 [master] pid:964 UIAutomatorWD->http://localhost:9001<-UIAutomatorWD
UIAutomatorWD http server ready
proxy.js:55:14 [master] pid:964 Proxy: /wd/hub/session/8beda65d-7ba5-4a93-9bec-8ae74277dc7c/element/1/value:POST to http://127.0.0.1:9001/wd/hub/session/8beda65d-7ba5-4a93-9bec-8ae74277dc7c/element/1/value:POST with body: {"value":["中文 +Test+12345678"]}
uiautomator-client.js:61:14 [master] pid:964
INSTRUMENTATION_STATUS_CODE: 0

proxy.js:85:20 [master] pid:964 Got response with status 200: {"status":13,"value":"An unknown server-side error occurred while processing the command.","sessionId":"8beda65d-7ba5-4a93-9bec-8ae74277dc7c"}
session.js:109:14 [master] pid:964 Send HTTP Respone to Client[2017-11-24 18:00:13]: {"status":13,"value":"\"An unknown server-side error occurred while processing the command.\"","sessionId":"8beda65d-7ba5-4a93-9bec-8ae74277dc7c"}
responseHandler.js:11:12 [master] pid:964 Recieve HTTP Request from Client[2017-11-24 18:00:13]: method: DELETE url: /wd/hub/session/8beda65d-7ba5-4a93-9bec-8ae74277dc7c, jsonBody: {}
session.js:80:12 [master] pid:964 Delete session, sessionId: 8beda65d-7ba5-4a93-9bec-8ae74277dc7c
responseHandler.js:49:14 [master] pid:964 Send HTTP Respone to Client[2017-11-24 18:00:13]: {"sessionId":"8beda65d-7ba5-4a93-9bec-8ae74277dc7c","status":0

iOS sample 是不是有问题,运行到切换 webview 出现 json 转换异常

com.alibaba.fastjson.JSONException: syntax error, pos 1, json : Internal Server Error

    at com.alibaba.fastjson.parser.DefaultJSONParser.parse(DefaultJSONParser.java:1361)
    at com.alibaba.fastjson.parser.DefaultJSONParser.parse(DefaultJSONParser.java:1268)
    at com.alibaba.fastjson.JSON.parse(JSON.java:137)
    at com.alibaba.fastjson.JSON.parse(JSON.java:128)
    at com.alibaba.fastjson.JSON.parseObject(JSON.java:201)
    at macaca.client.common.Utils.getRequest(Utils.java:69)
    at macaca.client.common.Utils.request(Utils.java:150)
    at macaca.client.commands.Context.getContexts(Context.java:27)
    at macaca.client.MacacaClient.contexts(MacacaClient.java:143)
    at macaca.client.IosSampleTest.switchToWebView(IosSampleTest.java:170)
    at macaca.client.IosSampleTest.webViewTest(IosSampleTest.java:117)
    at macaca.client.IosSampleTest.testCaseOne(IosSampleTest.java:58)
匿名 #39 · 2018年04月18日

模拟器下运行 macaca-java 的官方测试用例:报错如下
15:45:22.501 xcodebuild[7482:389241] Beginning test session XCTestWDUITests-76880428-A640-4127-BAE8-F5FFE2502DC3 at 2018-04-18 15:45:22.500 with Xcode 8E162 on target {
SimDevice: SimDevice : iPhone 7 (6D2B14B0-57D1-47D7-97A8-3E6E12813E74) : state={ Booted } deviceType={ SimDeviceType : com.apple.CoreSimulator.SimDeviceType.iPhone-7 } runtime={ SimRuntime : 10.3 (14E269) - com.apple.CoreSimulator.SimRuntime.iOS-10-3 }
} (10.3 (14E269))
15:45:22.501 xcodebuild[7482:389241] /Applications/Xcode.app/Contents/Developer/usr/bin/xcodebuild
clean
test
-project
/usr/local/lib/node_modules/macaca-ios/node_modules/_xctestwd@1.3.27@xctestwd/XCTestWD/XCTestWD.xcodeproj
-scheme
XCTestWDUITests
-destination
id=6D2B14B0-57D1-47D7-97A8-3E6E12813E74
XCTESTWD_PORT=8001
15:45:22.501 xcodebuild[7482:389241] Launching with Xcode.IDEFoundation.Launcher.PosixSpawn
15:45:22.504 xcodebuild[7482:389248] Sim iPhone 7: Unregistering for sim device notification with token 4
15:45:22.504 xcodebuild[7482:389248] Calling -[SimDevice getenv:error:] for TESTMANAGERD_SIM_SOCK
15:45:22.504 xcodebuild[7482:389248] Sim iPhone 7 has testmanagerd socket at /private/tmp/com.apple.launchd.L5RGnNsL2h/com.apple.testmanagerd.unix-domain.socket
15:45:22.505 xcodebuild[7482:389248] Validating test connection socket path (/private/tmp/com.apple.launchd.L5RGnNsL2h/com.apple.testmanagerd.unix-domain.socket)
15:45:22.505 xcodebuild[7482:389248] Constructing transport for test connection socket
15:45:22.505 xcodebuild[7482:389248] Connected socket 17 to testmanagerd for Sim iPhone 7
15:45:22.506 xcodebuild[7482:389248] Received connection for test runner session
15:45:22.506 xcodebuild[7482:389248] Listening for proxy connection request from the test bundle
15:45:22.507 xcodebuild[7482:389248] Initiating session with identifier: 76880428-A640-4127-BAE8-F5FFE2502DC3
15:45:22.508 xcodebuild[7482:389241] Received Ready from iOSSimulator
15:45:22.509 xcodebuild[7482:389241] Sending notification: NSConcreteNotification 0x7fc34692ee00 {name = com.apple.iphonesimulator.startSession; userInfo = {
deviceUDID = "6D2B14B0-57D1-47D7-97A8-3E6E12813E74";
}}
15:45:22.533 xcodebuild[7482:389241] Test runner session acquired connection.
15:45:22.533 xcodebuild[7482:389241] Waiting for test process to launch.
15:45:22.597 xcodebuild[7482:389241] Test operation failure: Test operation was canceled.
15:45:22.597 xcodebuild[7482:389241] _finishWithError:Error Domain=IDETestOperationsObserverErrorDomain Code=4 "Test operation was canceled." UserInfo={NSLocalizedDescription=Test operation was canceled.} didCancel: 1

使用 xcode 版本为 8.3.3,不知道是否有跟我一样的报错的同学帮解答下,多谢

Yinxl #40 · 2018年05月11日 Author
Emiya Shirou 回复

切换到 webview 异常一般都是因为 chromedriver 版本与 webview 版本不匹配 解决方案可参考:https://testerhome.com/topics/9651 常见问题

Yinxl #41 · 2018年05月11日 Author

不好意思 请更新最新版本 sample~ 问题已经解决

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

Yinxl Macaca-Java 版入门指南 中提及了此贴 10月29日 08:02
44楼 已删除
需要 登录 后方可回复, 如果你还没有账号请点击这里 注册