51Testing软件测试论坛

标题: 基于 uiRecorder 录制脚本的 自动化用例工程化模型 分享 [打印本页]

作者: 一纸荒年    时间: 2018-4-11 13:20
标题: 基于 uiRecorder 录制脚本的 自动化用例工程化模型 分享
不太会写,就简单直接的描述一下!希望对初学者上手会有些帮助;
其中还有许多需要完善和补充后面会陆续进行改进提交, 希望有同学能参与 补充完善!

首先需要感谢,macaca 项目组的各位大神 提供的这一系列的 测试工具; 感谢!

模型简介:
1.可以录制;
2.可以回放;
3.可以固化用例,多层分离管理!

简单操作方式如下:
录制:
uirecorder start --mobile sample/test.IOS3.js

回放:
source run.sh sample/test.IOS3.js

执行固化用例:
source run.sh testsuite/test.B2C.3.0.4.js

固化用例采用4层分离模式:
第一层: 页面元素数据层;
第二层:页面操作层;
第三层:用例层,或耦合关键字层;
第四层:suite层,业务场景层;

核心:
通用方法js模块;
commons.js

接入手机自动识别、app启动、用例执行、用例截图 等方法,这个是从录制脚本中抽离出来的,只多加了 reuse
参数 可以配置要不要预装app;
commBrowser.js
浏览器或手机操作方法;
commElem.js
被测元素对象公共操作方法;
commShould.js
公共断言方法;

分层结构如下图:
  1. 第一层:元素数据层
  2. 存放,元素对象的 xpath id 等;

  3. 如:

  4. exports.btnLoginPar = {
  5.         androidElem: '//*[@resource-id="com.ndol.sale.starter:id/btn_login"]',
  6.         iosElem: '//*[@name="登录/注册"]',
  7.         elementDesc: '登录/注册',
  8.         waitTime: 30000
  9. };

  10. // 商品分类 ,商品列表中 商品的 名称
  11. exports.goodsNamePar = {
  12.         androidElem: '//*[@resource-id="com.ndol.sale.starter:id/projuctTitle"]',
  13.         elementDesc: '商品名称'
  14. };
  15. 第二层:页面操作层
  16. 存放,对页面元素的操作方法;

  17. 如:

  18. exports.swipeGoodsName = function (type, index){commElem.waitElemAndSwipe(goodsPageUI.goodsNamePar, type, index, 5);};
  19. exports.swipeGoodsNameLastToFrist = function (){
  20.     commElem.waitElemAndSwipeLastToFrist(goodsPageUI.goodsNamePar);
  21. };
  22. 第三层:用例层,或耦合关键字层

  23. 如:

  24. exports.goodsListSwipeTest = function(){
  25.     //适用版本  3.0.0--3.0.4
  26.     commBrowser.testCaseDoc("验证商品列表 向上滑动,隐藏 列表上方 tab 和 下方 tab ");
  27.     unitPage.clickGoods();
  28.     goodsPage.clickCategoryXXSP();
  29.     // goodsPage.getGoodsNameSize();    //打印出 元素对象 的坐标信息
  30.     goodsPage.swipeGoodsName('up', 1);   //将第2个商品向上滑动
  31.     // commBrowser.swipeOnce(600,1000,600,400);   //滑屏
  32.     commShould.elemCanNotFind('全部');    // 全部 等 上方 tab 被隐藏
  33.     commShould.elemCanNotFind('分类');    // 分类 等 下方 tab 被隐藏
  34.     goodsPage.swipeGoodsName('down', 1);   //将第2个商品向下滑动
  35.     commShould.elemCanFind('全部');    // 全部 等 上方 tab 显示
  36.     commShould.elemCanFind('分类');    // 分类 等 下方 tab 显示

  37. };

  38. exports.loginTest = function(){
  39.     //适用版本  3.0.0--3.0.4
  40.     commBrowser.testCaseDoc("登录流程 ");
  41.     unitPage.clickMine();   //点击 “我的”
  42.     minePage.clickBtnLogin();   //点击注册登录
  43.     minePage.login(userName, password);
  44.     minePage.clickIvAvatar();   //点击 “我的资料”
  45.     unitPage.clickArrowBack();      //返回 我的
  46. };
  47. 第四层:suite层,业务场景层
  48. 启动被测app,运行测试前准备,执行用例 等;

  49. var commons = require('../commons/commons');
  50. var rootPath = commons.rootPath;
  51. var appPath = rootPath + '/app/b2c_v3.0.4_112.apk';
  52. function testcase(){
  53.     var b2c_mine = require('../testcase/mineTest');
  54.     var b2c_goods = require('../testcase/goodsTest');
  55.     var b2c_order = require('../testcase/orderTest');
  56.     var b2c_home = require('../testcase/homeTest');


  57.     // app安装完成,开始执行用例***************************
  58.     b2c_home.unloginDismissCouponTest();
  59.     b2c_home.unloginStatusToLoginTest();
  60.     b2c_order.unloginStatusToLoginTest();
  61.     b2c_mine.unloginStatusToLoginTest();
  62.     b2c_mine.userSettingTest();
  63.     b2c_goods.goodsListSwipeTest();
  64.     b2c_goods.findGoodsByName();
  65.     b2c_goods.addCartTest();
  66.     b2c_goods.addCartTest2();   
  67.     // ************登录后用例****************************
  68.     b2c_mine.loginTest();
  69.     b2c_mine.getUserInfo();
  70.     b2c_mine.loginStatusToLoginTest();
  71.     b2c_order.allOrderListTest();

  72.     // // ************退出登录****************************
  73.     b2c_mine.loginOffTest();

  74.     // 用例执行结束**************************************
  75. }


  76. if(module.parent && /mocha\.js/.test(module.parent.id)){
  77.     commons.runThisSpec(appPath, testcase);
  78. }

  79. function callSpec(name){
  80.     try{
  81.         require(rootPath + '/' + name)();
  82.     }
  83.     catch(e){
  84.         console.log(e)
  85.         process.exit(1);
  86.     }
  87. }


  88. 通用操作方法
  89. 如:

  90. /*
  91. *等待 对象的出现 ,向指定 方向 拖拽
  92. * @param androidElem 安卓对应的元素对象的获取方式 及 值
  93. * @param iosElem ios对应的元素对象的获取方式 及 值
  94. * @param elementDesc 控件描述文案
  95. * @param waitTime 等待元素对象刷新的 等待时间
  96. * @param type 滑动的方向 'up'、down'、'right'、'left'
  97. * @param index 元素的位标 从 0 开始
  98. * @param times 移动距离(宽或高)的倍数,默认1
  99. */
  100. exports.waitElemAndSwipe = function (parameter,type,index,times){
  101.     var androidElem = parameter.androidElem;
  102.     var iosElem = parameter.iosElem?parameter.iosElem:parameter.androidElem; //如果没有传ios的默认使用Android的
  103.     var elementDesc = parameter.elementDesc;
  104.     var waitTime = parameter.waitTime?parameter.waitTime:5000;
  105.     var index = index || 0;
  106.     var times = times || 1;
  107.     var type = type || 'up';
  108.     it('拖拽: 将第 ' + index + ' 个 ' + elementDesc + ' 元素,向' + type + '拖拽' + times + '个自身高度或宽度', function (){
  109.         if(platformName === 'Android'){
  110.             return driver.wait(androidElem, waitTime).get(index)
  111.                         .rect()
  112.                         .then(_get_element_rect)
  113.                         .then(function(par){
  114.                             // console.log(par);
  115.                             return _swipeElemByType(par, type, times);
  116.                         });
  117.         }else{
  118.             return driver.wait(iosElem, waitTime).get(index)
  119.                         .rect()
  120.                         .then(_get_element_rect)
  121.                         .then(function(par){
  122.                             // console.log(par);
  123.                             return _swipeElemByType(par, type, times);
  124.                         });
  125.         };
  126.     });
  127. };


  128. /*
  129. * 在当前页面 对 textAffirm 进行 未显示 断言
  130. * @param androidElem 安卓对应的元素对象的获取方式 及 值
  131. * @param iosElem ios对应的元素对象的获取方式 及 值
  132. * @param elementDesc 控件描述文案
  133. * @param waitTime 等待元素对象刷新的 等待时间
  134. * @param textAffirm 断言
  135. */
  136. exports.elemCanNotFind = function (textAffirm){
  137.     it('不存在断言:当前页面不存在文本元素对象 "' + textAffirm + '"', function (){
  138.         if(platformName === 'Android'){
  139.             return driver.find('//*[@text="' + textAffirm + '"]')
  140.                 .should.is.empty;    //判断当前页面没有找到该元素对象
  141.         }else{
  142.             return driver.find('//*[@name="' + textAffirm + '"]')
  143.                 .should.is.empty;
  144.         };
  145.     });
  146. };
复制代码

在后边说一些题外话!可以忽略!
开始使用 uiRecorder 录制,是为了 方便学习,后来发现录制完之后,用例显得特别臃肿庞大,
而且大量冗余,不方便工程化,就试着将录制的脚本进行固化、工程化,然后慢慢的就有了下面这些东西;

使用过程中 发现 对 IOS的录制和回放 相对 Android 要慢太多,希望各位大神 持续改进,好跟着喝汤!

所以模型中的东西,虽然是针对Android 和 IOS做方法统一,但实际上大部分只能Android跑通;

当然 我 用这一套的目的,也并不是要用来做 严谨的 业务逻辑UI自动化验证(这一块主要通过接口自动化实
现,已大量覆盖),
主要的目的是做一些:
日常 功能及功能页面巡检(每天定时跑个几次);
兼容性,就是接上不同的机型 各跑几次;
验证 一些 前端处理的简单逻辑 ;
等等


作者: llq_629    时间: 2019-7-9 10:24
相比于使用java或者python 手动编写的  有什么优势吗




欢迎光临 51Testing软件测试论坛 (http://bbs.51testing.com/) Powered by Discuz! X3.2