51Testing软件测试论坛

 找回密码
 (注-册)加入51Testing

QQ登录

只需一步,快速开始

微信登录,快人一步

手机号码,快捷登录

查看: 4538|回复: 1
打印 上一主题 下一主题

[讨论] 基于 uiRecorder 录制脚本的 自动化用例工程化模型 分享

[复制链接]
  • TA的每日心情
    奋斗
    2021-8-6 16:14
  • 签到天数: 1 天

    连续签到: 1 天

    [LV.1]测试小兵

    跳转到指定楼层
    1#
    发表于 2018-4-11 13:20:33 | 只看该作者 回帖奖励 |倒序浏览 |阅读模式
    不太会写,就简单直接的描述一下!希望对初学者上手会有些帮助;
    其中还有许多需要完善和补充后面会陆续进行改进提交, 希望有同学能参与 补充完善!

    首先需要感谢,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自动化验证(这一块主要通过接口自动化实
    现,已大量覆盖),
    主要的目的是做一些:
    日常 功能及功能页面巡检(每天定时跑个几次);
    兼容性,就是接上不同的机型 各跑几次;
    验证 一些 前端处理的简单逻辑 ;
    等等

    分享到:  QQ好友和群QQ好友和群 QQ空间QQ空间 腾讯微博腾讯微博 腾讯朋友腾讯朋友
    收藏收藏
    回复

    使用道具 举报

    该用户从未签到

    2#
    发表于 2019-7-9 10:24:13 | 只看该作者
    相比于使用java或者python 手动编写的  有什么优势吗
    回复 支持 反对

    使用道具 举报

    本版积分规则

    关闭

    站长推荐上一条 /1 下一条

    小黑屋|手机版|Archiver|51Testing软件测试网 ( 沪ICP备05003035号 关于我们

    GMT+8, 2024-11-22 03:31 , Processed in 0.061923 second(s), 22 queries .

    Powered by Discuz! X3.2

    © 2001-2024 Comsenz Inc.

    快速回复 返回顶部 返回列表