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
公共断言方法;
分层结构如下图:
第一层:元素数据层
存放,元素对象的 xpath id 等;
如:
exports.btnLoginPar = {
androidElem: '//*[@resource-id="com.ndol.sale.starter:id/btn_login"]',
iosElem: '//*[@name="登录/注册"]',
elementDesc: '登录/注册',
waitTime: 30000
};
// 商品分类 ,商品列表中 商品的 名称
exports.goodsNamePar = {
androidElem: '//*[@resource-id="com.ndol.sale.starter:id/projuctTitle"]',
elementDesc: '商品名称'
};
第二层:页面操作层
存放,对页面元素的操作方法;
如:
exports.swipeGoodsName = function (type, index){commElem.waitElemAndSwipe(goodsPageUI.goodsNamePar, type, index, 5);};
exports.swipeGoodsNameLastToFrist = function (){
commElem.waitElemAndSwipeLastToFrist(goodsPageUI.goodsNamePar);
};
第三层:用例层,或耦合关键字层
如:
exports.goodsListSwipeTest = function(){
//适用版本 3.0.0--3.0.4
commBrowser.testCaseDoc("验证商品列表 向上滑动,隐藏 列表上方 tab 和 下方 tab ");
unitPage.clickGoods();
goodsPage.clickCategoryXXSP();
// goodsPage.getGoodsNameSize(); //打印出 元素对象 的坐标信息
goodsPage.swipeGoodsName('up', 1); //将第2个商品向上滑动
// commBrowser.swipeOnce(600,1000,600,400); //滑屏
commShould.elemCanNotFind('全部'); // 全部 等 上方 tab 被隐藏
commShould.elemCanNotFind('分类'); // 分类 等 下方 tab 被隐藏
goodsPage.swipeGoodsName('down', 1); //将第2个商品向下滑动
commShould.elemCanFind('全部'); // 全部 等 上方 tab 显示
commShould.elemCanFind('分类'); // 分类 等 下方 tab 显示
};
exports.loginTest = function(){
//适用版本 3.0.0--3.0.4
commBrowser.testCaseDoc("登录流程 ");
unitPage.clickMine(); //点击 “我的”
minePage.clickBtnLogin(); //点击注册登录
minePage.login(userName, password);
minePage.clickIvAvatar(); //点击 “我的资料”
unitPage.clickArrowBack(); //返回 我的
};
第四层:suite层,业务场景层
启动被测app,运行测试前准备,执行用例 等;
var commons = require('../commons/commons');
var rootPath = commons.rootPath;
var appPath = rootPath + '/app/b2c_v3.0.4_112.apk';
function testcase(){
var b2c_mine = require('../testcase/mineTest');
var b2c_goods = require('../testcase/goodsTest');
var b2c_order = require('../testcase/orderTest');
var b2c_home = require('../testcase/homeTest');
// app安装完成,开始执行用例***************************
b2c_home.unloginDismissCouponTest();
b2c_home.unloginStatusToLoginTest();
b2c_order.unloginStatusToLoginTest();
b2c_mine.unloginStatusToLoginTest();
b2c_mine.userSettingTest();
b2c_goods.goodsListSwipeTest();
b2c_goods.findGoodsByName();
b2c_goods.addCartTest();
b2c_goods.addCartTest2();
// ************登录后用例****************************
b2c_mine.loginTest();
b2c_mine.getUserInfo();
b2c_mine.loginStatusToLoginTest();
b2c_order.allOrderListTest();
// // ************退出登录****************************
b2c_mine.loginOffTest();
// 用例执行结束**************************************
}
if(module.parent && /mocha\.js/.test(module.parent.id)){
commons.runThisSpec(appPath, testcase);
}
function callSpec(name){
try{
require(rootPath + '/' + name)();
}
catch(e){
console.log(e)
process.exit(1);
}
}
通用操作方法
如:
/*
*等待 对象的出现 ,向指定 方向 拖拽
* @param androidElem 安卓对应的元素对象的获取方式 及 值
* @param iosElem ios对应的元素对象的获取方式 及 值
* @param elementDesc 控件描述文案
* @param waitTime 等待元素对象刷新的 等待时间
* @param type 滑动的方向 'up'、down'、'right'、'left'
* @param index 元素的位标 从 0 开始
* @param times 移动距离(宽或高)的倍数,默认1
*/
exports.waitElemAndSwipe = function (parameter,type,index,times){
var androidElem = parameter.androidElem;
var iosElem = parameter.iosElem?parameter.iosElem:parameter.androidElem; //如果没有传ios的默认使用Android的
var elementDesc = parameter.elementDesc;
var waitTime = parameter.waitTime?parameter.waitTime:5000;
var index = index || 0;
var times = times || 1;
var type = type || 'up';
it('拖拽: 将第 ' + index + ' 个 ' + elementDesc + ' 元素,向' + type + '拖拽' + times + '个自身高度或宽度', function (){
if(platformName === 'Android'){
return driver.wait(androidElem, waitTime).get(index)
.rect()
.then(_get_element_rect)
.then(function(par){
// console.log(par);
return _swipeElemByType(par, type, times);
});
}else{
return driver.wait(iosElem, waitTime).get(index)
.rect()
.then(_get_element_rect)
.then(function(par){
// console.log(par);
return _swipeElemByType(par, type, times);
});
};
});
};
/*
* 在当前页面 对 textAffirm 进行 未显示 断言
* @param androidElem 安卓对应的元素对象的获取方式 及 值
* @param iosElem ios对应的元素对象的获取方式 及 值
* @param elementDesc 控件描述文案
* @param waitTime 等待元素对象刷新的 等待时间
* @param textAffirm 断言
*/
exports.elemCanNotFind = function (textAffirm){
it('不存在断言:当前页面不存在文本元素对象 "' + textAffirm + '"', function (){
if(platformName === 'Android'){
return driver.find('//*[@text="' + textAffirm + '"]')
.should.is.empty; //判断当前页面没有找到该元素对象
}else{
return driver.find('//*[@name="' + textAffirm + '"]')
.should.is.empty;
};
});
};
复制代码
在后边说一些题外话!可以忽略!
开始使用 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