该文为 macaca 的 nodejs 版本 样例https://github.com/macacajs/macaca-test-sample
macaca java 版本 https://github.com/macacajs/wd.java
macaca python 版本 https://github.com/macacajs/wd.py

类似于上一篇的思路。
https://testerhome.com/topics/5634

一 建立一个新用例脚本项目

D:\macaca\me-app

拷贝 D:\macaca\macaca-test-sample-master 官方示例脚本下的依赖文件 app 、package.json、 README.md
到新项目 me-app 下。为图方便。macaca-test 文件夹也整个 copy 过来。
您还需要将您的 apk 拷贝到目录\me-app\app 下。

二 创建用例脚本文件 *.test.js

D:\macaca\me-app\macaca-test
将示例脚本 macaca-mobile-sample.test.js 重命名为 android-acp-sample.test.js
开始编辑修改 android-acp-sample.test.js 脚本的内容。

三 编辑业务流脚本内容

3.1 用工具来定位元素

我们用到的是 macaca 团队刚发布的 app-inspector,关于该工具的安装部署请参考https://testerhome.com/topics/5626

3.2 android-acp-sample.test.js 脚本内容

只是简单的测试下,看是否可以如此构建自己的用例项目。所以业务流程只是简单的找到登录页 输入用户名 输入密码 点击登录而已 注释掉删除掉了一些验证步骤和自己不了解的原脚本内容。
其中截图实际是有问题的,因为环境目录设置问题。一会儿抽空看下调一下。

'use strict';

var path = require('path');
var _ = require('macaca-utils');

var platform = process.env.platform || 'Android';
platform = platform.toLowerCase();

var iOSOpts = {
  platformVersion: '9.3',
  deviceName: 'iPhone 5s',
  platformName: 'iOS',
  //bundleId: 'xudafeng.ios-app-bootstrap',
  app: path.join(__dirname, '..', 'app', `${platform}-app-bootstrap.zip`)
};

var androidOpts = {
  platformName: 'Android',
  //package: 'com.github.android_app_bootstrap',
  //activity: 'com.github.android_app_bootstrap.activity.WelcomeActivity',
  app: path.join(__dirname, '..', 'app', `your android apk.apk`)
};

var wd = require('webdriver-client')(_.merge({}, platform === 'ios' ? iOSOpts : androidOpts));

// override back for ios
wd.addPromiseChainMethod('customback', function() {
  if (platform === 'ios') {
    return this;
  }

  return this
    .back();
});

describe('macaca mobile sample', function() {
  this.timeout(5 * 60 * 1000);

  var driver = wd.initPromiseChain();

  driver.configureHttp({
    timeout: 600000
  });

  before(function() {
    return driver
      .initDriver();
  });

  after(function() {
    return driver
      .sleep(1000)
      .quit();
  });

  it('#1 Access 我的', function() {
    return driver
      .elementById('com.acp.aicaitencent:id/tvBottomTab4')
      .click()
      .sleep(1000);
  });

  it('#2 should display 我的page', function() {
    return driver
      .takeScreenshot();
  });

  it('#3 Access 立即登录/注册', function() {
    return driver
      .elementById('com.acp.aicaitencent:id/layoutLogin')
      .click()
      .sleep(1000);
  });

  it('#4 should display 登录Page', function() {
    return driver
      .takeScreenshot();
  });

  it('#5 Edit and Login', function() {
    return driver
      .elementById('com.acp.aicaitencent:id/etUserName')
      .sendKeys('***********')
      .sleep(5000)

      .elementById('com.acp.aicaitencent:id/etPwd')
      .sendKeys('***********')
      .sleep(5000)

      .elementById('com.acp.aicaitencent:id/tvLogin')
      .click()
      .sleep(5000)

      .takeScreenshot();
  });

});

PS:2016年8月12日补充
书接上文,今天早上来又看了一遍原作者的 macaca-mobile-sample.test.js 样例,原作者为了更好的演示,大家看

it('#7 should works with web', function() {
    return driver
      .webview()
      .elementById('index-kw')
      .sendKeys('中文+TesterHome')
      .elementById('index-bn')
      .tap()
      .sleep(5000)
      .source()
      .then(function(html) {
        html.should.containEql('TesterHome');
      })
      .takeScreenshot();
  });

  it('#8 should logout success', function() {
    return driver
      .native()
      .elementByName('PERSONAL')
      .click()
      .sleep(1000)
      .takeScreenshot()
      .elementByName('Logout')
      .click()
      .sleep(1000)
      .takeScreenshot();

.webview() .native() 大家明白了吧 这就是展示 webview 元素定位 和 native 原生元素定位。 所以大家还是多研究样例 先把简单的搞懂模仿起来,再进行复杂的,一步一步来。

四 运行环境准备工作

老样子,你需要到有 package.json 文件的 D:\macaca\me-app 目录下执行下 npm i 来为 me-app 准备 macaca 的组件包依赖。
成功看到安装成功日志后,接下来便可以执行用例脚本了。

五 执行用例脚本

先启动个安卓模拟器,此处我依然启动的是 Genymotion 模拟器。adb devices -l 查看下是否启动正常。

cd 到 macaca-test 目录下

D:\macaca\me-app\macaca-test>macaca run -d android-acp-sample.test.js --verbose
>> index.js:17:12 [master] pid:10376 webdriver server start with config:
 { port: 3456,
  window: true,
  ip: '*.*.*.*',
  host: 'cmd-PC',
  loaded_time: '2016-08-09 10:30:03' }
>> middlewares.js:17:10 [master] pid:10376 base middlewares attached
>> router.js:108:10 [master] pid:10376 router set
>> webdriver sdk launched
>>

>>
  macaca mobile sample

>> responseHandler.js:11:12 [master] pid:10376 Recieve HTTP Request from Client: method: POST url: /wd/hub/session, jsonBody: {"desiredCapabilities":{"autoAcceptAlerts":true,"platformName":"Android","app":"D:\\macaca\\me-app\\app\\your android apk.apk","browserName":"firefox","version":"","javascriptEnabled":true,"platform":"ANY"}}
>> session.js:47:10 [master] pid:10376 Creating session, sessionId: b7ff1a6b-c532-40fe-b5c8-074f24b2d0b0.
>> helper.js:191:12 [master] pid:10376 Using local app form D:\macaca\me-app\app\your android apk.apk
INSTRUMENTATION_STATUS: numtests=1
INSTRUMENTATION_STATUS: stream=
com.android.uiautomator.client.Initialize:
INSTRUMENTATION_STATUS: id=UiAutomatorTestRunner
INSTRUMENTATION_STATUS: test=testStartServer
INSTRUMENTATION_STATUS: class=com.android.uiautomator.client.Initialize
INSTRUMENTATION_STATUS: current=1
>> socket server ready
>> socket client ready
recive: {"cmd":"wake","args":{}}

return: {"success":true,"data":true}

>> responseHandler.js:43:14 [master] pid:10376 Send HTTP Respone to Client: {"sessionId":"b7ff1a6b-c532-40fe-b5c8-074f24b2d0b0","status":0,"value":"{\"autoAcceptAlerts\":true,\"platformName\":\"Android\",\"app\":\"D:\\\\macaca\\\\me-app\\\\app\\\\your android apk.apk\",\"browserName\":\"firefox\",\"version\":\"\",\"javascriptEnabled\":true,\"platform\":\"ANY\",\"window\":true}"}
>> responseHandler.js:11:12 [master] pid:10376 Recieve HTTP Request from Client: method: POST url: /wd/hub/session/b7ff1a6b-c532-40fe-b5c8-074f24b2d0b0/element, jsonBody: {"using":"id","value":"com.acp.aicaitencent:id/tvBottomTab4"}
recive: {"cmd":"find","args":{"strategy":"id","selector":"com.acp.aicaitencent:id/tvBottomTab4","multiple":false}}

return: {"success":true,"data":{"ELEMENT":"1"}}
>> responseHandler.js:43:14 [master] pid:10376 Send HTTP Respone to Client: {"sessionId":"b7ff1a6b-c532-40fe-b5c8-074f24b2d0b0","status":0,"value":"{\"ELEMENT\":\"1\"}"}


>> responseHandler.js:11:12 [master] pid:10376 Recieve HTTP Request from Client: method: POST url: /wd/hub/session/b7ff1a6b-c532-40fe-b5c8-074f24b2d0b0/element/1/click, jsonBody: {}
recive: {"cmd":"click","args":{"elementId":"1"}}

return: {"success":true,"data":true}
>> responseHandler.js:43:14 [master] pid:10376 Send HTTP Respone to Client: {"sessionId":"b7ff1a6b-c532-40fe-b5c8-074f24b2d0b0","status":0,"value":"true"}


>>
>>#1 Access 我的 (1269ms)

>> responseHandler.js:11:12 [master] pid:10376 Recieve HTTP Request from Client: method: GET url: /wd/hub/session/b7ff1a6b-c532-40fe-b5c8-074f24b2d0b0/screenshot, jsonBody: {}
>> responseHandler.js:50:12 [master] pid:10376 Send Error Respone to Client: Error: Command failed: C:\WINDOWS\system32\cmd.exe /s /c "D:\Android\android-sdk-windows/platform-tools/adb -s 192.168.236.101:5555 pull /data/local/tmp/screenshot.png C:\Users\cmd\AppData\Local\Temp\macaca-android-screenshot11679-10376-150nd76.png"

>> responseHandler.js:56:14 [master] pid:10376 Error: Command failed: C:\WINDOWS\system32\cmd.exe /s /c "D:\Android\android-sdk-windows/platform-tools/adb -s 192.168.236.101:5555 pull /data/local/tmp/screenshot.png C:\Users\cmd\AppData\Local\Temp\macaca-android-screenshot11679-10376-150nd76.png"

    at ChildProcess.exithandler (child_process.js:213:12)
    at emitTwo (events.js:87:13)
    at ChildProcess.emit (events.js:172:7)
    at maybeClose (internal/child_process.js:827:16)
    at Process.ChildProcess._handle.onexit (internal/child_process.js:211:5)

  Error: Command failed: C:\WINDOWS\system32\cmd.exe /s /c "D:\Android\android-sdk-windows/platform-tools/adb -s 192.168.236.101:5555 pull /data/local/tmp/screenshot.png C:\Users\cmd\AppData\Local\Temp\macaca-android-screenshot11679-10376-150nd76.png"

      at ChildProcess.exithandler (child_process.js:213:12)
      at emitTwo (events.js:87:13)
      at ChildProcess.emit (events.js:172:7)
      at maybeClose (internal/child_process.js:827:16)
      at Process.ChildProcess._handle.onexit (internal/child_process.js:211:5)

>>
>>     1) #2 should display 我的page

>> responseHandler.js:11:12 [master] pid:10376 Recieve HTTP Request from Client: method: POST url: /wd/hub/session/b7ff1a6b-c532-40fe-b5c8-074f24b2d0b0/element, jsonBody: {"using":"id","value":"com.acp.aicaitencent:id/layoutLogin"}
recive: {"cmd":"find","args":{"strategy":"id","selector":"com.acp.aicaitencent:id/layoutLogin","multiple":false}}

return: {"success":true,"data":{"ELEMENT":"2"}}

>> responseHandler.js:43:14 [master] pid:10376 Send HTTP Respone to Client: {"sessionId":"b7ff1a6b-c532-40fe-b5c8-074f24b2d0b0","status":0,"value":"{\"ELEMENT\":\"2\"}"}
>> responseHandler.js:11:12 [master] pid:10376 Recieve HTTP Request from Client: method: POST url: /wd/hub/session/b7ff1a6b-c532-40fe-b5c8-074f24b2d0b0/element/2/click, jsonBody: {}
recive: {"cmd":"click","args":{"elementId":"2"}}

return: {"success":true,"data":true}

>> responseHandler.js:43:14 [master] pid:10376 Send HTTP Respone to Client: {"sessionId":"b7ff1a6b-c532-40fe-b5c8-074f24b2d0b0","status":0,"value":"true"}
>>
>>#3 Access 立即登录/注册 (1441ms)

>> responseHandler.js:11:12 [master] pid:10376 Recieve HTTP Request from Client: method: GET url: /wd/hub/session/b7ff1a6b-c532-40fe-b5c8-074f24b2d0b0/screenshot, jsonBody: {}
>> responseHandler.js:50:12 [master] pid:10376 Send Error Respone to Client: Error: Command failed: C:\WINDOWS\system32\cmd.exe /s /c "D:\Android\android-sdk-windows/platform-tools/adb -s 192.168.236.101:5555 pull /data/local/tmp/screenshot.png C:\Users\cmd\AppData\Local\Temp\macaca-android-screenshot11679-10376-14ljgkf.png"

>> responseHandler.js:56:14 [master] pid:10376 Error: Command failed: C:\WINDOWS\system32\cmd.exe /s /c "D:\Android\android-sdk-windows/platform-tools/adb -s 192.168.236.101:5555 pull /data/local/tmp/screenshot.png C:\Users\cmd\AppData\Local\Temp\macaca-android-screenshot11679-10376-14ljgkf.png"

    at ChildProcess.exithandler (child_process.js:213:12)
    at emitTwo (events.js:87:13)
    at ChildProcess.emit (events.js:172:7)
    at maybeClose (internal/child_process.js:827:16)
    at Process.ChildProcess._handle.onexit (internal/child_process.js:211:5)

  Error: Command failed: C:\WINDOWS\system32\cmd.exe /s /c "D:\Android\android-sdk-windows/platform-tools/adb -s 192.168.236.101:5555 pull /data/local/tmp/screenshot.png C:\Users\cmd\AppData\Local\Temp\macaca-android-screenshot11679-10376-14ljgkf.png"

      at ChildProcess.exithandler (child_process.js:213:12)
      at emitTwo (events.js:87:13)
      at ChildProcess.emit (events.js:172:7)
      at maybeClose (internal/child_process.js:827:16)
      at Process.ChildProcess._handle.onexit (internal/child_process.js:211:5)

    2) #4 should display 登录Page

>> responseHandler.js:11:12 [master] pid:10376 Recieve HTTP Request from Client: method: POST url: /wd/hub/session/b7ff1a6b-c532-40fe-b5c8-074f24b2d0b0/element, jsonBody: {"using":"id","value":"com.acp.aicaitencent:id/etUserName"}
recive: {"cmd":"find","args":{"strategy":"id","selector":"com.acp.aicaitencent:id/etUserName","multiple":false}}

return: {"success":true,"data":{"ELEMENT":"3"}}

>> responseHandler.js:43:14 [master] pid:10376 Send HTTP Respone to Client: {"sessionId":"b7ff1a6b-c532-40fe-b5c8-074f24b2d0b0","status":0,"value":"{\"ELEMENT\":\"3\"}"}
>> responseHandler.js:11:12 [master] pid:10376 Recieve HTTP Request from Client: method: POST url: /wd/hub/session/b7ff1a6b-c532-40fe-b5c8-074f24b2d0b0/element/3/value, jsonBody: {"value":["*****"]}
recive: {"cmd":"setText","args":{"elementId":"3","text":"******"}}

return: {"success":true,"data":true}

>> responseHandler.js:43:14 [master] pid:10376 Send HTTP Respone to Client: {"sessionId":"b7ff1a6b-c532-40fe-b5c8-074f24b2d0b0","status":0,"value":"true"}
>> responseHandler.js:11:12 [master] pid:10376 Recieve HTTP Request from Client: method: POST url: /wd/hub/session/b7ff1a6b-c532-40fe-b5c8-074f24b2d0b0/element, jsonBody: {"using":"id","value":"com.acp.aicaitencent:id/etPwd"}
recive: {"cmd":"find","args":{"strategy":"id","selector":"com.acp.aicaitencent:id/etPwd","multiple":false}}

return: {"success":true,"data":{"ELEMENT":"4"}}

>> responseHandler.js:43:14 [master] pid:10376 Send HTTP Respone to Client: {"sessionId":"b7ff1a6b-c532-40fe-b5c8-074f24b2d0b0","status":0,"value":"{\"ELEMENT\":\"4\"}"}
>> responseHandler.js:11:12 [master] pid:10376 Recieve HTTP Request from Client: method: POST url: /wd/hub/session/b7ff1a6b-c532-40fe-b5c8-074f24b2d0b0/element/4/value, jsonBody: {"value":["*****"]}
recive: {"cmd":"setText","args":{"elementId":"4","text":"*********"}}

return: {"success":true,"data":true}

>> responseHandler.js:43:14 [master] pid:10376 Send HTTP Respone to Client: {"sessionId":"b7ff1a6b-c532-40fe-b5c8-074f24b2d0b0","status":0,"value":"true"}
>> responseHandler.js:11:12 [master] pid:10376 Recieve HTTP Request from Client: method: POST url: /wd/hub/session/b7ff1a6b-c532-40fe-b5c8-074f24b2d0b0/element, jsonBody: {"using":"id","value":"com.acp.aicaitencent:id/tvLogin"}
recive: {"cmd":"find","args":{"strategy":"id","selector":"com.acp.aicaitencent:id/tvLogin","multiple":false}}

return: {"success":true,"data":{"ELEMENT":"5"}}
>> responseHandler.js:43:14 [master] pid:10376 Send HTTP Respone to Client: {"sessionId":"b7ff1a6b-c532-40fe-b5c8-074f24b2d0b0","status":0,"value":"{\"ELEMENT\":\"5\"}"}


>> responseHandler.js:11:12 [master] pid:10376 Recieve HTTP Request from Client: method: POST url: /wd/hub/session/b7ff1a6b-c532-40fe-b5c8-074f24b2d0b0/element/5/click, jsonBody: {}
recive: {"cmd":"click","args":{"elementId":"5"}}


return: {"success":true,"data":true}

>> responseHandler.js:43:14 [master] pid:10376 Send HTTP Respone to Client: {"sessionId":"b7ff1a6b-c532-40fe-b5c8-074f24b2d0b0","status":0,"value":"true"}
>> responseHandler.js:11:12 [master] pid:10376 Recieve HTTP Request from Client: method: GET url: /wd/hub/session/b7ff1a6b-c532-40fe-b5c8-074f24b2d0b0/screenshot, jsonBody: {}
>> responseHandler.js:50:12 [master] pid:10376 Send Error Respone to Client: Error: Command failed: C:\WINDOWS\system32\cmd.exe /s /c "D:\Android\android-sdk-windows/platform-tools/adb -s 192.168.236.101:5555 pull /data/local/tmp/screenshot.png C:\Users\cmd\AppData\Local\Temp\macaca-android-screenshot11679-10376-cm89un.png"

>> responseHandler.js:56:14 [master] pid:10376 Error: Command failed: C:\WINDOWS\system32\cmd.exe /s /c "D:\Android\android-sdk-windows/platform-tools/adb -s 192.168.236.101:5555 pull /data/local/tmp/screenshot.png C:\Users\cmd\AppData\Local\Temp\macaca-android-screenshot11679-10376-cm89un.png"

    at ChildProcess.exithandler (child_process.js:213:12)
    at emitTwo (events.js:87:13)
    at ChildProcess.emit (events.js:172:7)
    at maybeClose (internal/child_process.js:827:16)
    at Process.ChildProcess._handle.onexit (internal/child_process.js:211:5)

  Error: Command failed: C:\WINDOWS\system32\cmd.exe /s /c "D:\Android\android-sdk-windows/platform-tools/adb -s 192.168.236.101:5555 pull /data/local/tmp/screenshot.png C:\Users\cmd\AppData\Local\Temp\macaca-android-screenshot11679-10376-cm89un.png"

      at ChildProcess.exithandler (child_process.js:213:12)
      at emitTwo (events.js:87:13)
      at ChildProcess.emit (events.js:172:7)
      at maybeClose (internal/child_process.js:827:16)
      at Process.ChildProcess._handle.onexit (internal/child_process.js:211:5)

    3) #5 Edit and Login

>> responseHandler.js:11:12 [master] pid:10376 Recieve HTTP Request from Client: method: DELETE url: /wd/hub/session/b7ff1a6b-c532-40fe-b5c8-074f24b2d0b0, jsonBody: {}
>> macaca-android.js:60:14 [master] pid:10376 Error: Command failed: C:\WINDOWS\system32\cmd.exe /s /c "D:\Android\android-sdk-windows/platform-tools/adb emu kill"
error: no emulator detected

    at ChildProcess.exithandler (child_process.js:213:12)
    at emitTwo (events.js:87:13)
    at ChildProcess.emit (events.js:172:7)
    at maybeClose (internal/child_process.js:827:16)
    at Process.ChildProcess._handle.onexit (internal/child_process.js:211:5)
>> session.js:80:12 [master] pid:10376 Delete session, sessionId: b7ff1a6b-c532-40fe-b5c8-074f24b2d0b0
>> responseHandler.js:43:14 [master] pid:10376 Send HTTP Respone to Client: {"sessionId":"b7ff1a6b-c532-40fe-b5c8-074f24b2d0b0","status":0}
>>

>>
  2 passing (1m)
  3 failing

  1) macaca mobile sample #2 should display 我的page:
     Error: [takeScreenshot()] Not JSON response
      at exports.newError (D:\macaca\me-app\node_modules\macaca-wd\wd\lib\utils.js:139:13)
      at D:\macaca\me-app\node_modules\macaca-wd\wd\lib\callbacks.js:59:17
      at D:\macaca\me-app\node_modules\macaca-wd\wd\lib\webdriver.js:179:5
      at Request._callback (D:\macaca\me-app\node_modules\macaca-wd\wd\lib\http-utils.js:87:7)
      at Request.self.callback (D:\macaca\me-app\node_modules\macaca-wd\node_modules\request\request.js:368:22)
      at Request.<anonymous> (D:\macaca\me-app\node_modules\macaca-wd\node_modules\request\request.js:1219:14)
      at IncomingMessage.<anonymous> (D:\macaca\me-app\node_modules\macaca-wd\node_modules\request\request.js:1167:12)
      at endReadableNT (_stream_readable.js:921:12)

  2) macaca mobile sample #4 should display 登录Page:
     Error: [takeScreenshot()] Not JSON response
      at exports.newError (D:\macaca\me-app\node_modules\macaca-wd\wd\lib\utils.js:139:13)
      at D:\macaca\me-app\node_modules\macaca-wd\wd\lib\callbacks.js:59:17
      at D:\macaca\me-app\node_modules\macaca-wd\wd\lib\webdriver.js:179:5
      at Request._callback (D:\macaca\me-app\node_modules\macaca-wd\wd\lib\http-utils.js:87:7)
      at Request.self.callback (D:\macaca\me-app\node_modules\macaca-wd\node_modules\request\request.js:368:22)
      at Request.<anonymous> (D:\macaca\me-app\node_modules\macaca-wd\node_modules\request\request.js:1219:14)
      at IncomingMessage.<anonymous> (D:\macaca\me-app\node_modules\macaca-wd\node_modules\request\request.js:1167:12)
      at endReadableNT (_stream_readable.js:921:12)

  3) macaca mobile sample #5 Edit and Login:
     Error: [takeScreenshot()] Not JSON response
      at exports.newError (D:\macaca\me-app\node_modules\macaca-wd\wd\lib\utils.js:139:13)
      at D:\macaca\me-app\node_modules\macaca-wd\wd\lib\callbacks.js:59:17
      at D:\macaca\me-app\node_modules\macaca-wd\wd\lib\webdriver.js:179:5
      at Request._callback (D:\macaca\me-app\node_modules\macaca-wd\wd\lib\http-utils.js:87:7)
      at Request.self.callback (D:\macaca\me-app\node_modules\macaca-wd\node_modules\request\request.js:368:22)
      at Request.<anonymous> (D:\macaca\me-app\node_modules\macaca-wd\node_modules\request\request.js:1219:14)
      at IncomingMessage.<anonymous> (D:\macaca\me-app\node_modules\macaca-wd\node_modules\request\request.js:1167:12)
      at endReadableNT (_stream_readable.js:921:12)

>> Test completed!


以下为参考的文章,向作者表示感谢,提供了思路。
如何从头编写你的 Macaca 测试用例
编写移动端 Macaca 测试用例 [单步调试]
Macaca-iOS 入门那些事 2
[基于 Node.js 的自动化测试-Macaca] - 自动化测试实践总结

更多信息请参考 wiki 汇总

小马的 macaca 入门指引合集


↙↙↙阅读原文可查看相关链接,并与作者交流