uiautomator自动化测试原理和实现过程

uiautomator自动化测试原理和实现过程

2023年6月29日发(作者:)

uiautomator⾃动化测试原理和实现过程uiautomator⾃动化测试原理和实现过程博主博客⽹站: ⼲货满满原理阐述 本⼈从事过很长⼀段时间的⾃动化测试,其中安卓的⾃动化主要选⽤的uiautomator框架,我这⾥阐述⼀下uiautomator⾃动化测试的原理,从整体⼊⼿、化繁为简,可以轻松理解这款框架精妙之处。就相当于有了屋⼦的图纸,再分步来添砖加⽡就容易的多。 UIAutomator是Google开发的⾃动化测试⼯具,⽆需源代码,可在不同App间调度;并不需要知道程序内部的结构,通过界⾯来点击、返回、退出等来对程序进⾏测试,所以它也是⿊盒测试。 uiautomator,把这个单词拆开ui+auto+mator,就很容易理解了,是基于UI界⾯然后对某个控件操作的⾃动化;利⽤uiautomator框架获取到APP 的UI(界⾯)上各种元素,然后实现对此app各种操作。从⾃带的控件获取⼯具uiautomatorview就可以看出来APP控件名称和⽅法等。可以看出来,每⼀个APP图标就是⼀个控件(如果这⾥⾯出现了不能识别中⽂,把安卓设备⾃带替换成最新的就⾏了) uiautomator框架是⽤Java语⾔编写的,也是三段体的,⼀个完整⾃动化过程必须包含setup 、⾃动化⽅法、 teardown 三个部分;这就像⽕车⼀样,有车头(setup)、车⾝(⽅法)、车尾(teardown);车⾝(⾃动化⽅法体)具体有⼏节、是载货还是拉⼈这个每次都由⾃⼰编写代码来定义;如果⽅法所在的类中没有setup、teardown,⾃动化执⾏此⽅法时,它会直接使⽤(继承的)⽗类的setup和teardown,如果⽗类都没有setup和teardown,它会执⾏uiautomator框架顶级类中setup和teardown;setup⼀般⽤来初始化,teardown⽤来复位;⽐如你上次⾃动化停留在某款APP的设置界⾯;再次执⾏新的⾃动化⽅法时,先初始化到home;这样就能保证左右滑动后能找到其他APP;teardown复位呢,就相当于⽤例执⾏完成后停留APP设置界⾯,执⾏teardown复位到此APP的主页;同样车头(setup)和车尾(teardown)也可以被改造(重构) ,定制化出来的⽕车能更好的适应实际⽣产情况。就像同⼀个UI(界⾯)就可以定义为⼀个特殊的⽕车,如设置界⾯中,这个界⾯所有控件的测试⽅法可以写到⼀个类中,共⽤setup和teardown;初始化(车头)都是打开应⽤进⼊我的-设置界⾯,并把所有开关按钮置为关闭状态;复位(复位)都是点击两次返回按钮进⼊到APP的初始主页中。然后再来编写某个控件测试的⽅法。这种三段体架构的精髓在于执⾏⽅法即可,初始化和复位⽆需重复编写,且⾃动执⾏,如果初始化和复位写的⽐较规范,相当于测试下⼀条⽤例时前置条件有了双保险;代码展⽰setup重构代码:@Override protected void setUp() throws Exception { ();

initDeviceParams(); callShell("rm -rf /data/anr"); callShell("rm -rf /data/tombstones"); initCaseFolder(); registerCommonWatcher(); if (!enOn()) { (); sleepSec(1); }

launchHomeApp(); }⽅法体代码:@CaseName("点击快捷菜单中的天⽓区域") public void clickWeatherArea() throws UiObjectNotFoundException, RemoteException {

addStep("进⼊快捷菜单页⾯"); clickExpandButtonOnStatusBar(); sleepSec(2);

addStep("发现时间区域,进⼊快捷菜单"); UiObject clockBTonstatusbar= new UiObject (new UiSelector().className("ew") .resourceId("ui:id/clock")); verify("没有发现账号登录按钮,进⼊快捷菜单失败",());//验证功能

addStep("点击天⽓区域"); (384, 173); sleepSec(2);

addStep("发现天⽓⽚段,进⼊天⽓应⽤"); UiObject check_weatherpage= new UiObject (new UiSelector().className("Layout") .resourceId("r:id/fragment_weather")); verify("没有进⼊天⽓应⽤",check_());

}

⼀定要编写好验证的⽅法(verify,也可以叫其他⽅法名),这点⾮常重要;脚本执⾏完成后,⽤例是否达到预期要⽤verify⽐对⼀下。teardown代码:@Override protected void tearDown() throws Exception { boolean ANRFlag = false; boolean TombstoneFlag = false;

int errorTypeCount = 0; callShell("/system/bin/sh /data/local/tmp/ " + caseFolder); callShell("mv /data/tombstones " + caseFolder); File tombstones = new File(caseFolder + tor + "tombstones/tombstone_00"); File anr = new File(caseFolder + tor + "anr"); File anr = new File(caseFolder + tor + "anr"); callShell("mv -r /data/anr " + caseFolder); if (()) { ANRFlag = true; errorTypeCount = errorTypeCount + 1; UiObject anrWindows = new UiObject(new UiSelector().className("ew").textContains("isn't responding")); if (()) { send_status(STATUS_ANR, "ANR", "Detected ANR when running case"); takeScreenshot(); try { new UiObject(new UiSelector().className("").text("OK")).click(); } catch (UiObjectNotFoundException e) { tackTrace(); Log.e(logTag,"OK button in anr window is gone. Is it recoverd?"); } finally { } Log.i(logTag,"ANR has been catched and Device recovery normally"); } else { takeScreenshot(); Log.i(logTag, "Can not find ANR note"); } } if (()) { TombstoneFlag = true; errorTypeCount = errorTypeCount + 1; send_status(STATUS_TOMBSTONES, "TOMBSTONES", "Detected TOMBSTONES when running case"); takeScreenshot(); }

unregisterCommonWatcher(); switch (errorTypeCount) { case 1: if (ANRFlag == true) { Log.i(logTag, "AutoZM TestCase ended: ANR Occurred"); fail("ANR occurred"); } if (TombstoneFlag == true) { Log.i(logTag, "AutoZM TestCase ended: Tombstones Occurred"); fail("TOMBSTONES occurred"); } break; case 2: if (ANRFlag == true && TombstoneFlag == true) { Log.i(logTag, "AutoZM TestCase ended: Tombstones and ANR Occurred"); fail("TOMBSTONES and ANR occurred"); } break; default: Log.i(logTag, "AutoZM TestCase ended successfully"); break; }

callShell("logcat -v time -d -f " + caseFolder + tor + ""); callShell("logcat -c");

wn(); } uiautomator基础对象:UiScrollable UiObject UiSelector UiDevice等这⾥不做描述,⽹上有很多相关资料,主要是博主这⼏年精⼒基本在运维⽅⾯,不知道是否有变化。找到某个控件⼀般通过UISelector().[text,textContains,resourceId,classname]都⾏,根据实际情况来选择,⽐如text名称都⼀样,就换⼀个⽅法来选择控件;这些属性,uiautomatorview⾥⾯都可以获取到。还有⼀点 就是长按,这个⾃带的⽅法,如果不好使;就改⽤滑动来实现,在按钮上长时间滑动达到长按的效果。脚本执⾏单个⽅法执⾏⽐较简单:1)把编译出来的jar包,copy到安卓设备adb push /home/workspace/tvCar/bin/ /data/local/tmp/2)执⾏⽅法adb shell uiautomator runtest -c #testHdTel(执⾏某⼀条⽤例⽅法)。如果⽤例需要传参数,执⾏时把参数输进去adb shell uiautomator runtest -c ser#testLoginuser -e account 152******** -e PWD zhenglin ;

批量执⾏和报告如果只能上⾯单条⽤例,⼿动执⾏,那⾃动化也就是个弱鸡,啥也⼲不了;所以更近⼀步:我们可以吧所有⽅法list写到⼀个表格或者txt中,然后⽤shell读取后,批量执⾏⾃动化⽤例,把执⾏结果写成html,这个是重点难点;要分步实现:批量执⾏⾃动化⽤例⽤例列表,把编写好的⾃动化⽅法存储到⼀个txt列表中####changename编写批量执⾏脚本 ,由于博客篇幅限制,这⾥节选最重要的⼀段代码for ((i=1;i<=$caselistloop;i++))do for line in `cat ` do testCase=`echo $line| awk -F "," '{print $1}'` case_name=`echo $line|awk -F# '{print $2}'|awk -F "," '{print $1}'` …… echo ====================================== $line start ====================================== adb -s ${IPADDRESS} shell "uiautomator runtest -c $testCase --nohup -e disable_ime true $globalvars $case_avg -e caseFolder $current_case_folder >> sdcard/" …… donedonecaselistloop 为⽤例执⾏遍数⽣成报告这个⽐较复杂,本⼈采⽤的是其他团队基于(Python)Django框架编写的模板,然后来⽣成出html格式的报告,⽽且是⼏年前的框架了,这⾥就不做源码展⽰了;⼀般⼤公司都会有现成⾃动化报告框架,我们负责把⾃动化代码编写完成,提交到仓库,cicd会⾃动把这⼀套运⾏起来。对⼩型公司来说,可以到GitHub上寻找开源的模板,然后稍作修改后使⽤;uiautomator⾃动化的局限性uiautomator框架需要安卓sdk的⽀持,sdk包⾮常⼤,下载还需要科学上⽹;编写还要有java语⾔功底,捕获UI控件来进⾏各种操作。最⼤的⼀个短板:如果界⾯是动态的,⽆法正常捕获到控件,采⽤这个框架,什么也操作不了。⽣成报告要借助第三⽅⼯具或者模板。uiautomator⾃动化最适⽤场景是冒烟测试,那些重要的测试⽤例,那些改动不⼤的界⾯采⽤此框架进⾏⾃动化测试,再合适不过了。如果界⾯和控件名称改动频繁,即便⽤uiautomator实现了⾃动化测试,也得不偿失(可能会累死⾃动化测试⼯程师)。附:uiautomator学习⽹站

发布者:admin,转转请注明出处:http://www.yc00.com/web/1688019686a67318.html

相关推荐

发表回复

评论列表(0条)

  • 暂无评论

联系我们

400-800-8888

在线咨询: QQ交谈

邮件:admin@example.com

工作时间:周一至周五,9:30-18:30,节假日休息

关注微信