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

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

目前在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;
共收到 85 条回复
4f9cc2

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

F8621b
Samuel.ZhaoY · 2楼 · 2017年09月12日 作者
4f9cc2harsayer 回复

稍等

F8621b
Samuel.ZhaoY · 3楼 · 2017年09月12日 作者
4f9cc2harsayer 回复

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

F8621b
Samuel.ZhaoY · 4楼 · 2017年09月12日 作者

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

798276

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

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

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

F8621b
Samuel.ZhaoY · 9楼 · 2017年09月12日 作者
798276utopia 回复

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

2457
xdf · 10楼 · 2017年09月12日
  • 钩子配置没文档啊
  • 持续集成实践文档也需要补充
F8621b
Samuel.ZhaoY · 11楼 · 2017年09月12日 作者
2457xdf 回复

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

3165

强烈关注!!

4f9cc2

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

F8621b
4f9cc2harsayer 回复

恩 会尽快完善

12758
yyf · 15楼 · 2017年09月12日

非常棒

14700

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

2457
xdf · 17楼 · 2017年09月12日
14700dongwenhua 回复

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

F8621b
Samuel.ZhaoY · 18楼 · 2017年09月12日 作者
14700dongwenhua 回复

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

F8621b
Samuel.ZhaoY · 19楼 · 2017年09月12日 作者
14700dongwenhua 回复

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

13930

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

F8621b
13930Test_Sir 回复

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

5512

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

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

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

F8621b
Samuel.ZhaoY · 26楼 · 2017年09月12日 作者
55120x88 回复

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

3170
F8621bSamuel.ZhaoY 回复

👍 👍 👍 ,已star

F8621b
Samuel.ZhaoY · 28楼 · 2017年09月12日 作者

互相学习, 欢迎contribute 啊

F8621b
Samuel.ZhaoY · 29楼 · 2017年09月12日 作者

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

104

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

1522fe
cay · 32楼 · 2017年09月12日

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

F8621b
Samuel.ZhaoY · 33楼 · 2017年09月12日 作者

好的

F8621b
Samuel.ZhaoY · 34楼 · 2017年09月12日 作者
104seveniruby 回复

谢谢前辈鼓励

6853

我喜欢那个report

14148

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

F4d75c
F8621bSamuel.ZhaoY 回复

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

F8621b
Samuel.ZhaoY · 38楼 · 2017年09月13日 作者
141481717p 回复

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

F8621b
Samuel.ZhaoY · 39楼 · 2017年09月13日 作者
F4d75cxiaoshouzi 回复

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

F4d75c
F8621bSamuel.ZhaoY 回复

就是测试用的示例app,bootstrap

8074

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

F8621b
Samuel.ZhaoY · 43楼 · 2017年09月14日 作者
F4d75cxiaoshouzi 回复

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

14148
F8621bSamuel.ZhaoY 回复

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

F8621b
Samuel.ZhaoY · 45楼 · 2017年09月14日 作者
141481717p 回复

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

Ee76af

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

14148
F8621bSamuel.ZhaoY 回复

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

F8621b
Samuel.ZhaoY · 48楼 · 2017年09月15日 作者
Ee76afkasi 回复

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

F8621b
Samuel.ZhaoY · 49楼 · 2017年09月15日 作者
141481717p 回复

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

14148
F8621bSamuel.ZhaoY 回复

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

10998

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

F8621b
Samuel.ZhaoY · 52楼 · 2017年09月18日 作者
10998huangejuan 回复

欢迎contribute 以及提issue.

69ae3c

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

F8621b
Samuel.ZhaoY · 54楼 · 2017年09月19日 作者
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
Samuel.ZhaoY · 60楼 · 2017年09月22日 作者
1cfb31zzljeky 回复

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

F8621b
Samuel.ZhaoY · 61楼 · 2017年09月22日 作者
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’参数,然后调用不起来

4f9cc2

@Samuel.ZhaoY

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

4f9cc2

@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

4f9cc2


想问下配置的写法:
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
Samuel.ZhaoY · 74楼 · 2017年11月01日 作者
592plateau520 回复

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

F8621b
Samuel.ZhaoY · 75楼 · 2017年11月01日 作者
32rockyrock 回复

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

F8621b
Samuel.ZhaoY · 76楼 · 2017年11月01日 作者
A7ed86dreamer.li 回复

目前模拟器比较稳定

F8621b
Samuel.ZhaoY · 78楼 · 2017年11月01日 作者
4f9cc2harsayer 回复

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
Samuel.ZhaoY · 83楼 · 2017年11月13日 作者
592plateau520 回复

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

947815

能开放下Dockerfile文件?docker中运行后,reports中只看到了截图,html报告怎么生成?另外登陆如果是webview怎么处理,有没有相应的操作?还有测试下来,提供的用例在全屏手机上login操作不了。

4f9cc2

@Samuel.ZhaoY 可以指导下
permissionPatterns: '[\"继续安装\",\"下一步\",\"好\",\"允许\",\"确定\",\"我知道\"]'

该句的 具体源码路径 和原理吗? 这个很有用

F8621b
Samuel.ZhaoY · 86楼 · 2018年01月03日 作者
4f9cc2harsayer 回复

这个能力属于macaca 安卓驱动 desiredcapability 的能力, 为了解决安装过程中 授权页面窗口定制化, 阻挡了安装使用而设计的. 具体源码要参考 uiautomator 里面, 唤起安卓测试驱动以后的一段代码. 通过NoSmoke -> macaca-android -> uiautomator

2825

这个有实现白名单和黑名单了吗

104 seveniruby IDEA debug scala sbt project AppCrawler 中提及了此贴 01月18日 16:31
需要 登录 后方可回复, 如果你还没有账号请点击这里 注册