Macaca Macaca 自动遍历器 NoSmoke 发布公测

Samuel.ZhaoY · 发布于 2017年09月12日 · 最后由 Samuel.ZhaoY 回复于 2017年11月13日 · 4219 次阅读
本帖已被设为精华帖!

目前在macaca 提供的基础能力上研发出了一套多端深度遍历爬虫工具. 希望可以最大化减少UI 测试脚本的编写
涵盖以下功能点:

  1. 支持iOS, Android,PC-Web 三个平台的自动化测试
  2. 同时可以通过以下三种层级方式, 根据使用者自身场景满足不同程度的定制化需求:
    2.1 YML 配置文件: 普通内容定制 轻度定制
    2.2 钩子: 个别流程,拦截定制 中度定制
    2.3 平台.js 文件覆盖: 按需选择流程接口, 覆盖定制 重度定制
  3. Mokey 的测试过程与macaca-reporter 联通,测试数据由macaca-report 报表分析器统一沉淀并进行可视化展示.
    (目前PC-Web , 以及 Reporter 对爬虫的支持还在调试当中)

项目源码地址: https://github.com/macacajs/NoSmoke
macaca-reporter 源码地址: https://github.com/macacajs/macaca-reporter

目前项目正在收尾阶段, 所以先发一个原型出来,希望收集一下大家的意见,发布版本会根据大家的意见进行调优.

1.运行效果

1.1 iOS

1.2 Android

1.3 PC-Web

2. 启动指令:

项目依赖:
请确保你安装了macaca 系列的各个开源库:

npm i macaca-cli
npm i macaca-wd
npm i macaca-chrome
npm i macaca-android
npm i macaca-ios
git clone git@github.com:macacajs/NoSmoke.git
cd NoSmoke
npm install

2.1 配置文件

对于新手,建议直接使用NoSmoke 项目中public 路径下配置好的yml 文件, 里面标注了iOS/Android/Web-PC 三种爬取对象的简易配置, 可以直接跑起来

2.2 命令行

NoSmoke 根目录执行

macaca server --verbose

新开命令行窗口, 在NoSmoke 根目录执行

npm run dev

3. 定制化说明

3.1 为了大家更好理解NoSmoke 的实现原理,提供一份详细爬行流程图,.

screen shot 2017-09-06 at 6 00 16 pm

3.2 YML 配置文件选项详解:

# 1. Initialization option
desiredCapabilities:
  platformName: 'platform iOS/Android'
  deviceName: 'name of the device'
  app: 'url for downloading app here'

# 2. Crawling option
crawlingConfig:
  platform: 'iOS'
  targetElements:
    loginAccount:
      actionType  : 'action type: 1-click; 2-input'
      searchValue : 'the value to search'
      actionValue : 'the value to input'
  exclusivePattern: 'pushView/popView'
  clickTypes: 
    - 'array of clickable UI types: StaticText/Button'
  editTypes:
    - 'array of editable UI types: SecureTextField/TextFiled'
  horizontalScrollTypes:
    - 'array of horizontal scrollable UI types: PageIndicator'
  verticalScrollTypes:
    - 'array of vertical scrollable UI types: ScrollView'
  tabBarTypes:
    - 'array of control widget which behaves like a master in the 
    master-detail view structures: TabBar'
  exclusiveTypes:
    - 'array of disabled and esclusive UI types: NavigationBar'
  navigationBackKeyword:
    - 'array of words on which items should be regarded 
    as a back button: back'

3.3 钩子定制:

再精密设计的深度遍历算法对于不同的UI 界面设计, 也不能完全保证能够cover 大部分的遍历场景,因此通过钩子的形式给使用者提供不干预流程的前提下,定制遍历可能性
用户可以通过定制 /public/hooks.js 中的各个函数 对默认行为进行定制:

function Hooks(config, sessionId) {}

/**
 * Method to generate a unique digest which can identify a window, change the node.digest if you want.
 * @Params: $platform the name of the platform "iOS/android/web"
 * @Params: $source the raw json source of the current page
 * @Params: $node the node which needs to settle digest
 * @Returns: true to indicate the action has been handled and the default logic will not execute
 * to indicate the action has been handled
 * */
Hooks.prototype.checkDigest = function(platform, source, node, crawler) {
  return false;
};

/**
 * Method to sort a list of actions which will be later bind to a crawling node object, return the list of actions.
 * @Params: actions the array of actions which can be further sorted.
 * @Params: crawler the crawler instance which contains the context information as well as crawler config.
 * @Returns: actions the sorted actions which should be bind to the crawling node.
 * */
Hooks.prototype.sortActionPriority = function(actions, crawler) {
  return actions;
};

/**
 * Method to perform action for the current platform.
 * @Params: actions the actions which belongs to current active node, user can determine the priority of action execution
 * @Params: crawler the crawler instance which contains the context information as well as crawler config
 * @Returns: true to indicate the action has been handled and the default logic will not execute
 * */
Hooks.prototype.performAction = function(actions, crawler) {
  return false;
};

/**
 * Method to analysis and insert tab nodes for a master-detail view structure.
 * @Params: sourceArray the array of elements which belongs to the candidate tab node.
 * @Params: crawler the crawler instance which contains the context information as well as crawler config
 * @Returns: true to indicate the action has been handled and the default logic will not execute
 * */
Hooks.prototype.insertTabNode = function (sourceArray, crawler) {
  return false;
};

3.4 函数覆盖:

用户可以参考crawler.js 延伸的 android.js, ios.js, web.js 对 crawler 中特定行为进行覆盖定制, 比如说 ios.js 中对个别接口针对iOS平台做了定制:

let NSCrawler = require('./crawler').NSCrawler;

// Parent must be an array of child elements
NSCrawler.prototype.insertXPath = function (parent, child) {
  let prefix = this.config.platform === 'iOS' ? 'XCUIElementType' : '';

  /** scan and check index path for once and only once*/
  let currentTypeCount = child.type + '_count';
  if (!parent[currentTypeCount]) {
    for (let i = 0; i < parent.children.length; i++) {
      currentTypeCount = parent.children[i].type + '_count';
      if (!parent[currentTypeCount]) {
        parent[currentTypeCount] = 1;
      } else {
        parent[currentTypeCount]++;
      }
      parent.children[i].pathInParent = parent[currentTypeCount];
    }
  }

  let currentIndex = child.pathInParent;
  child.xpath = (parent.xpath ? parent.xpath : '//' + prefix + 'Application[1]') + '/' + prefix + child.type + '[' + currentIndex + ']';
};

exports.NSCrawler = NSCrawler;
共收到 81 条回复
7555

请开始你的表演。(有的章节还是空的,图呢?)

F8621b
7555harsayer 回复

稍等

F8621b
7555harsayer 回复

好了, 过滤了一下无关内容

F8621b

目前还在测试阶段, 欢迎大家试用提供反馈.

798276

本质上还是monkey么?比monkey瞎点相比,更有目标的点?

110 Lihuazhang 将本帖设为了精华贴 09月12日 15:55
2457

我理解的爬虫类测试应该是一种特殊规则的monkey吧,属于monkey test范畴

F8621b
798276utopia 回复

这个是通过分析窗口树,生成节点, 通过深度遍历算法点击每一个按钮, 同一个按钮不会点击两遍, 一层一层往下层界面进行遍历. 并且支持多个平台, 对接了报告收集器. 跟一般的随机mokey 不是一个层面的东西.

2457
  • 钩子配置没文档啊
  • 持续集成实践文档也需要补充
F8621b
2457xdf 回复

钩子的接口注释就是说明, 要用某一个钩子的时候直接在那段接口里面写你的实现.

3165

强烈关注!!

7555

3.2 3.3 3.4 这些 最好能举例 举例具体的 然后 怎么根据示例写配置 或 改这些配置

F8621b
7555harsayer 回复

恩 会尽快完善

12758

非常棒

14700

游戏的元素一个界面就是一坨 是不是和这个无缘了
😂

2457
14700dongwenhua 回复

非view体系的,我觉得可以指定位置,或者定制一个monkey规则,看场景吧

F8621b
14700dongwenhua 回复

可以试试, 因为深度遍历本身只是一套算法, 至于怎么筛选元素,怎么定位, 都应该是可以根据平台定制的.

F8621b
14700dongwenhua 回复

遍历的一个关键点是 区分不同的视觉窗口,对于UI 变动频率高的页面, 可以使用钩子里的 checkDigest 方法用你自己的实现区分页面

13930

😃 很棒...想问下, 自动遍历速度快吗?还是和目前执行用例速度相当.

F8621b
13930Test_Sir 回复

因为涉及到 界面分析的操作,还有页面比对,会慢一些, 但不影响使用

5512

我想问一下,在遍历中突然的弹窗界面怎么处理?另外按一下返回直接跳到两级的界面的这种遍历怎么处理?

23楼 已删除
F8621b
55120x88 回复
  1. 目前会自动取消alert 弹窗. 2. 调到两级的意思是什么? 窗口连续跳了两次吗?爬行的时候是检索最近这一次窗口的元素信息,识别窗口的,中间那次短时间的窗口会被忽略
5512
F8621bSamuel.ZhaoY 回复

我的意思是按一下物理返回会返回两个层级的界面,你会怎么处理?我这边也是关了弹窗,但就是有些弹窗里面的控件也是需要遍历,这个有点蛋疼。

F8621b
55120x88 回复

设计的时候有考虑这个case, 比如说对于 tabview 这样的 master - detail 视窗结构, 你可以在config 中配置什么样的element type 是一个 上层页面控件, 这样扫描的时候会把一个页面分成两层node , controller 这层在上面一个node , 对应每一个tab 的子视窗是一个单独的node , 完成子视窗的遍历以后, 就会选取tab bar 本身 进行遍历

3170
F8621bSamuel.ZhaoY 回复

👍 👍 👍 ,已star

F8621b

互相学习, 欢迎contribute 啊

F8621b

后续计划配置几个比较有名的app , 测试一下爬行效果

104

不错 又多一个优秀的项目 这块还能做的更好 自动遍历跟很多场景都可以有效结合

1522fe

期待有一款优秀的APP遍历工具

F8621b

好的

F8621b
104seveniruby 回复

谢谢前辈鼓励

6853

我喜欢那个report

14148

也做了一个类似的东西。想请问一下,iOS 自动遍历保证不会重复点击的逻辑- -如果切换到别的页面,遍历完又返回到之前的页面后,怎么确定原页面的已被点击的元素

F4d75c
F8621bSamuel.ZhaoY 回复

好厉害。
我这边试了下,启动示例app后,一直停留在登录页,并没有输入信息,请问是怎么回事呢?

F8621b
141481717p 回复

需要缓存已经爬取过的页面 ,下一次到这个页面的时候只点击没有触发的element

F8621b
F4d75cxiaoshouzi 回复

具体原因具体分析了, 你也可以吧模拟器运行的 apk 或者 .app 给我我试试

F4d75c
F8621bSamuel.ZhaoY 回复

就是测试用的示例app,bootstrap

8074

感谢楼主分享,回头试试看,和现有的其他自动遍历工具比较试试。

F8621b
F4d75cxiaoshouzi 回复

需要检查一下环境,以及使用的yml 详细配置, 看下有没有具体的log. 应该是环境问题

14148
F8621bSamuel.ZhaoY 回复

请问是说缓存整个树结构吗?

F8621b
141481717p 回复

恩 不是整个tree, 是这个里面有效的信息模型

Ee76af

请问在ios10以上的版本,这个是否支持滑屏按照一定规则进行解锁呢? 目前已知appium的版本遍历工具是不支持

14148
F8621bSamuel.ZhaoY 回复

明白了,多谢。等周末具体读一下:)
目前做法类似,取树中的key做处理来做key。但是有一个问题我没有想明白。如果想优化执行速度,从哪几个方面来入手比较好。目前想过的有缓存页面元素树,但是不像 Android 可以拿到对应的 activity,没有太好的头绪,请问有这样的想法吗?请教一下

F8621b
Ee76afkasi 回复

要自己设定操作了. 目前还没有支持到这个功能上来. 不过可以通过复写performAction 这个函数实现

F8621b
141481717p 回复

有兴趣的话 欢迎一起做这个项目,窗口识别的问题, 目前通过实现一个窗口digest 的方式去比对, 不过生成digest 的函数现在也暴露出来, 可以自己定制, 比如说支持模糊度识别等等

14148
F8621bSamuel.ZhaoY 回复

已fork
周末读代码学习一下 哈哈:)

10998

@Samuel.ZhaoY 我也在做类似这样的工作,你说的通过窗口digest的方式去对比,有详细的策略吗?可以加你讨论下不 哈?

F8621b
10998huangejuan 回复

欢迎contribute 以及提issue.

69ae3c

mark 太棒了 问个问题 就是这个深度遍历算法 是广度优先还是深度优先

F8621b
69ae3cpliue 回复

目前做的 深度优先.

69ae3c
F8621bSamuel.ZhaoY 回复

xctestwd 啥时候能解决下 查找元素 id为中文时乱码的BUG呀

1cfb31

你好,我最近也在做ios自动化遍历测试,过程中碰到一些问题:
1 app在申请权限的时候,客户端无法给appium发送消息
2 有些界面弹出对话框,比如取消和确定,点击确定变成取消,用curl post消息的形式点击坐标也是点击的坐标为确定,实际点击为取消,ios底层还是app应用对这些操作做了限制 ,这样会导致遍历一直退不出来的情况

1cfb31

我的思路和你的是相反的,采用递归来实现,但是难点就在于递归的层次和返回页面层次要保持一致。
iso 遍历2小时左右就已经很烫了,还会出现自动关机的情况,设置遍历的深度和时间很有必要。

69ae3c
1cfb31zzljeky 回复

感觉如果是递归的话 不太符合用户的操作的习惯吧

1cfb31
69ae3cpliue 回复

这点很有道理~~

F8621b
1cfb31zzljeky 回复

没错, 需要设置时间,最大深度,每个页面对多元素数量, 并且对元素类型进行筛选, 可以有效的提高爬行效率 减少运算成本

F8621b
69ae3cpliue 回复

抱歉, 公司业务太忙, 还要搞nosmoke, 会尽快看的 请谅解

9698

使用 sudo 执行了 npm i macaca_ios -g 还是提示无权限

error: Unable to create directory: /usr/local/lib/node_modules/macaca-ios/node_modules/devicelog/build/devicelog.build/Release/devicelog.build (Permission denied)

【求救】

69ae3c
F8621bSamuel.ZhaoY 回复

我在github上回复了 我目前的暂时做法

6de0ca

请问web系统的遍历如何做?能否给个例子!

1263

不错,可以试试看。

69ae3c
9698y693055797 回复

cd /usr/local/lib/node_modules/macaca-ios
然后chomd -R 777

A7ed86

可以支持真机吗,我在后面加了‘udid’参数,然后调用不起来

7555

@Samuel.ZhaoY

我今天试了下,发现两个问题 已经报了issue 帮我看下 https://github.com/macacajs/NoSmoke/issues/28

7555

@Samuel.ZhaoY https://github.com/macacajs/NoSmoke/issues/27
我看这个人的问题 和我一样啊 也是唤起了测试app 但执行不了 输入 那以后的步骤

-----> Crawling Finished <-----
(node:4352) UnhandledPromiseRejectionWarning: Unhandled promise rejection (rejection id: 1): TypeError: socketed.disconne
cted is not a function

7555


想问下配置的写法:
targetElements:
loginAccount:
actionType : 'action type: 1-click; 2-input'
searchValue : 'the value to search'
actionValue : 'the value to input'

我看你的样例 和 配置写法 又实际看了下测试app的 元素 不清楚 你配置里的 loginAccount 是怎么定位的 为什么写loginAccount

targetElements: 是指被操作元素的话 但这里看不出 你后边写 loginAccount 对应的事app-inspector里的哪个元素。能解释下吗?
@Samuel.ZhaoY

96

安卓好像跑不起来啊??

592

按照默认配置修改,android跑不起来,只显示一个report页面,页面上一只猴子😂

F8621b
592plateau520 回复

麻烦试试最新的代码, 之前驱动有些不稳定

F8621b
32rockyrock 回复

试试现在的代码, 使用模拟器

F8621b
A7ed86dreamer.li 回复

目前模拟器比较稳定

F8621b
7555harsayer 回复

loginAccount 这层只是用来语义化 特定元素的,没有特别数据结构上的含义

592
F8621bSamuel.ZhaoY 回复

您好,试了最新的代码,执行npm run dev命令后,就跳转到report页面,页面是空白的 没有app调起

592

@Samuel.ZhaoY android能跑起来了,启动app后不会输入帐号密码登录进去 一直在登录界面

592

-----> Crawling Finished <-----
(node:19628) UnhandledPromiseRejectionWarning: Unhandled promise rejection (rejection id:
1): TypeError: process.exist is not a function

运行起来很快就结束了 没有输入进行任何操作

592

@Samuel.ZhaoY 例子跑通了,换用自己的app去跑,出现以下错误,请帮忙看看,谢谢

socket connected
(node:23480) UnhandledPromiseRejectionWarning: Unhandled promise rejection (rejection id:
2): FetchError: invalid json response body at http://localhost:3456/wd/hub/session reason:

Unexpected token I in JSON at position 0

UIAutomatorWD http server ready
responseHandler.js:56:12 [master] pid:24084 Send Error Respone to Client: Error: Comman
d failed: C:\gaoyuan\sdk/platform-tools/adb -s emulator-5554 install -r "C:\Users\YOYO.ma
caca-temp\amap_2.8.2.1018_publish_signed_201711080850_82ac783355b740ac984a05b34a53e3eae894
8d05_auto.apk"
Failed to install C:\Users\YOYO.macaca-temp\amap_2.8.2.1018_publish_signed_201711080850_8
2ac783355b740ac984a05b34a53e3eae8948d05_auto.apk: Failure [INSTALL_FAILED_NO_MATCHING_ABIS
: Failed to extract native libraries, res=-113]

responseHandler.js:62:14 [master] pid:24084 Error: Command failed: C:\gaoyuan\sdk/platf
orm-tools/adb -s emulator-5554 install -r "C:\Users\YOYO.macaca-temp\amap_2.8.2.1018_publ
ish_signed_201711080850_82ac783355b740ac984a05b34a53e3eae8948d05_auto.apk"
Failed to install C:\Users\YOYO.macaca-temp\amap_2.8.2.1018_publish_signed_201711080850_8
2ac783355b740ac984a05b34a53e3eae8948d05_auto.apk: Failure [INSTALL_FAILED_NO_MATCHING_ABIS
: Failed to extract native libraries, res=-113]

at ChildProcess.exithandler (child_process.js:198:12)
at emitTwo (events.js:106:13)
at ChildProcess.emit (events.js:191:7)
at maybeClose (internal/child_process.js:920:16)
at Process.ChildProcess._handle.onexit (internal/child_process.js:230:5)

Error: Command failed: C:\gaoyuan\sdk/platform-tools/adb -s emulator-5554 install -r "C:
\Users\YOYO.macaca-temp\amap_2.8.2.1018_publish_signed_201711080850_82ac783355b740ac984a0
5b34a53e3eae8948d05_auto.apk"
Failed to install C:\Users\YOYO.macaca-temp\amap_2.8.2.1018_publish_signed_201711080850
_82ac783355b740ac984a05b34a53e3eae8948d05_auto.apk: Failure [INSTALL_FAILED_NO_MATCHING_AB
: Failed to extract native libraries, res=-113]

at ChildProcess.exithandler (child_process.js:198:12)
at emitTwo (events.js:106:13)
at ChildProcess.emit (events.js:191:7)
at maybeClose (internal/child_process.js:920:16)
at Process.ChildProcess._handle.onexit (internal/child_process.js:230:5)

F8621b
592plateau520 回复

你安装的apk 文件使用了.so 文件, 你使用的安卓模拟器架构与 .so 支持的模拟器架构不符 https://stackoverflow.com/questions/24572052/install-failed-no-matching-abis-when-install-apk. 适配一下你的模拟器架构

需要 登录 后方可回复, 如果你还没有账号请点击这里 注册