zoukankan      html  css  js  c++  java
  • 【测试】安卓自动化测试代码片段Java

    【前言】

    编写安卓自动化测试代码,本文选择的是夜神模拟器+Appium

    【语言选择】

    Appium使用的是C/S架构方式,Client端可以支持的编程语言挺多的,本文选择的是Java

    【IDE选择】

    编写Java代码,本文选择的IDE是eclipse

    【eclipse基础配置】

    使用Maven管理项目的依赖包

    【依赖包】

    在网站(https://mvnrepository.com/)中搜索需要安装的依赖包的Maven代码放入pom.xml文件中

    【需要的依赖包】

    1 java-client

    2 testNG

    启动夜神模拟器中的驾考宝典app的配置:

    // 1 创建配置对象
    DesiredCapabilities desiredCapabilities = new DesiredCapabilities();
    
    // 2 添加配置-测试的设备
    desiredCapabilities.setCapability("deviceName", "127.0.0.1:62001");
    // 2 添加配置-测试的平台
    desiredCapabilities.setCapability("platformName", "Android");
    // 2 添加配置-测试的App
    desiredCapabilities.setCapability("appPackage", "com.handsgo.jiakao.android");
    // 2 添加配置-测试App的启动入口
    desiredCapabilities.setCapability("appActivity", "com.handsgo.jiakao.android.splash.Login");
    
    // 3 创建驱动(参数:Appium通讯地址 配置参数)
    androidDriver = new AndroidDriver<WebElement>(new URL("http://127.0.0.1:4723/wd/hub"), desiredCapabilities);

    更改配置,解决输入框不能输入数据的问题

    // 2 添加配置-更改自动化引擎来解决输入框输入不了数据的问题
    desiredCapabilities.setCapability("automationName", "uiautomator2");

    更改配置,不清除应用的数据启动测试(默认和设置为true时为不清除)

    desiredCapbilities.setCapability("noReset", "true");

    元素等待

    在自动化过程中,元素的出现受到网络环境,设备性能等多种因素影响。因此元素加载的时间可能不一致,从而会导致元素无法定位超时报错。

    因此设置元素等待可以更加灵活的制定等待定位元素的时间,从而增强脚本的健壮性,提高执行效率。

    元素等待方式1:强制等待

    固定的等待时间

    Thread.sleep(8000);//等待8秒

    元素等待方式2:隐式等待

    针对全局元素设置等待时间,如果没有定位到UI元素,脚本可以在全局等待时间长度内进行反复查询定位,定位成功后会提前结束等待,继续接下来的执行任务

    androidDriver.manage().timeouts().implicitlyWait(30, TimeUnit.SECONDS);

    元素等待方式3:显示等待

    针对某个元素设置等待时间

    WebDriverWait webDriverWait = new WebDriverWait(androidDriver, 10);
    WebElement webElement = webDriverWait.until(new ExpectedCondition<WebElement>() {
        @Override
        public WebElement apply(WebDriver arg0) {
            return  androidDriver.findElementById("com.handsgo.jiakao.android:id/btn_agree");
        }
    });
    webElement.click();    

    定位UI元素的方式

    定位方式1:ID定位

    ID就是一个控件的唯一身份标识,由开发人员在项目中指定,如果有对应的resource-id,可以采取这种方式来实现定位操作。

    注意:有可能app开发人员并不是很严谨,一个界面会出现多个相同的id,这种情况下默认定位到第一个有此id的元素。

    测试用例代码示例:

    androidDriver.findElementById("com.handsgo.jiakao.android:id/btn_agree").click();

    如果有多个相同id的情况:

    List<WebElement> listElements = androidDriver.findElementById("com.handsgo.jiakao.android:id/btn_agree");
    
    listElements.get(2).click();

    定位方式2:text定位

    Appium 1.5之前的版本支持By.name方式,比如:

    // 根据text属性进行定位
    androidDriver.findElementByname("上海");

    Appium 1.5之后的版本不再支持By.name方式,需要使用UIAutonator原生自动化引擎,比如:

    // 使用UIAutomator定位text属性元素
    androidDriver.findElementByAndroidUIAutomator("new UiSelector().text("上海")");

    【注意】如果该方式调用不出,检查一下jre的版本是否在1.5以上,更改方式如下:

    第一步:

     第二步:

     第三步:

    定位方式3:className定位(弃用)

    根据class属性定位元素,一般在页面中很多元素的class属性一致,所以这种方式基本上不使用

    定位方式4:xpath定位

    Appium也可以支持xpath定位,在项目中此种方式能够使用多场景,xpath定位在web自动化测试中可以使用在app自动化测试中也可以使用,xpath定位有两种定位方式,一种是绝对位置定位另一种是相对位置定位,相对位置定位使用更加普遍,因为如果使用绝对位置定位UI元素有所改变的情况下,相关UI元素的xpath都需要相应进行改变。xpath相对路径需要使用到class属性和text属性内容进行拼接:

    androidDriver.findElementByXPath("//android.widget.TextView[@text='上海']").click();

    定位方式5:accessibility id定位

    在UIAutomatorViewer并没有此属性,对应是content-desc属性 。

    注意:很多UI元素没有这个属性

    androidDriver.findElementByAccessibilityId("上海").click();

    定位方式6:坐标定位

    选择设置中关于手机-连续点击五次版本号-进入开发者选项-勾选指针位置

    注意:坐标定位受设备屏幕尺寸/分辨率/DPI影响,万不得已不要使用此种方式定位元素

    定位方式7:Toast元素的获取

    获取要求:Java-client 5.0+、使用UIAutomator2自动化引擎、Android系统版本5.0+

    获取方法:

    WebElement toastElement = androidDriver.findElementByXPath("//*[contains(@text, '用户名与密码')]");
    System.out.print(toastElement.getText());

    对UI元素的操作

    UI元素操作1:点击操作

    .click();//点击

    UI元素操作2:输入操作

    .sendKeys("深圳");//输入

    UI元素操作3:滑动手势

    Java-clilent 5.0之前版本提供的滑动API

    // Java-client 4.1.2
    androidDriver.swipe(356, 594, 356, 794, 800);
    // (起始点坐标x, 起始点坐标y, 终止点坐标x, 终止点坐标y, 滑动时间)

    Java-client 5.0之后需要自定义实现(可采用之前版本的实现思路)

    // Java-client 6.1.0
    TouchAction touchAction = new TouchAction(androidDriver);
    
    PointOption startPointOption = PointOption.point(356, 594);
    PointOption endPointOption = PointOption.point(356, 794);
    Duration duration = Duration.ofMillis(800);
    WaitOptions waitOptions = WaitOptions.waitOptions(duration);
    
    touchAction.press(startPointOption).waitAction(waitOptions).moveTo(endPointOption).release();
    touchAction.perform();// 让滑动生效

    滑动的使用场景:下拉刷新、手势解锁等

    手势解锁需要滑动到多个点,代码上使用多个.moveTo()方法就行了:

    touchAction.press(pointOption1).waitAction(waitOptions).moveTo(pointOption2).moveTo(pointOption3).moveTo(pointOption4).release();

    UI元素操作4:多点触摸手势

    使用场景:地图缩放

    MultiTouchAction类可以模拟用户多点触摸操作

    主要包含有add()和perform()两个方法

    可以结合TouchAction模拟多根手指滑动效果

    // 1 实例化MultiTouchAction对象
    MultiTouchAction multiTouchAction = new MultiTouchAction(androidDriver);
    // 2 实例化两个TouchAction(因为需要两根手指进行放大操作)
    TouchAction touchAction1 = new TouchAction<>(androidDriver);
    TouchAction touchAction2 = new TouchAction<>(androidDriver);
    // 3 得到当前屏幕的高度和宽度
    int x = androidDriver.manage().window().getSize().getWidth();
    int y = androidDriver.manage().window().getSize().getHeight();
    // 第一根手指的滑动
    touchAction1.press(PointOption.point(x * 4 / 10, y * 4 / 10))
    .waitAction(WaitOptions.waitOptions(Duration.ofMillis(1000)))
    .moveTo(PointOption.point(x * 2 / 10, y * 2 / 10)).release(); // 第二根手指的滑动 touchAction2.press(PointOption.point(x * 6 / 10, y * 6 / 10))
    .waitAction(WaitOptions.waitOptions(Duration.ofMillis(1000)))
    .moveTo(PointOption.point(x * 8 / 10, y * 8 / 10)).release(); // 把两根手指的动作添加到MultiTouchAction里面 multiTouchAction.add(touchAction1).add(touchAction2); multiTouchAction.perform();

    断言

    使用testNG集成断言框架 ,选择src/test/java中的.java文件,右击选择“testNG”-->“Convert to TestNG”,如下图所示

     (如果右击没有TestNG选项,可以在eclipse中的MarketPlace中搜索TestNG在线安装)

    之后在项目中会自动生成一个testng.xml文件,在刚刚选中的java文件中会在一些方法前面有@Test标识,测试开始之前的初始化@BeforeTest标识,刻可以将之前放在main函数中的执行代码放置到beforeTest标识下的自定义setup方法中,然后把main函数去掉。使用@AfterTest标识的函数用来当测试用例运行完毕后销毁测试驱动(调用测试驱动对象的quit方法)。此后运行方式使用"Run As"--"TestNG Test"。

    断言1:检查当前展示的界面是否是测试用例预期的结果

    先确定预期界面类的名字,终端执行

    adb shell dumpsys activity | find "mFocusedActivity"

    输出的结果中,斜杠后面的一长串就是类名,例如下图所示

    在测试用例中:

    String expected = "cn.mucang.android.mars.student.refactor.business.inquiry.activity.InquiryActivuty";
    String actual = androidDriver.currentActivity();
    Assert.assertEquals(actual, expected);

    运行完之后会有一个测试报告自动生成。

    Appium常用API

    Appium常用API1:实现页面跳转(startActivity)

    页面跳转,包括App内部页面及App相互跳转

    // 开启某一个activity,实现跳转
    // 首先我们需要创建activity对象,用activity构建方法初始化,参数为对应的包名和类名
    Activity activity = new Activity("com.lemon.lemonban", "com.lemon.lemonban.LoginActivity");
    androidDriver.startActivity(activity);

    注意:如找不到startActivity方法,请确认你的eclipse java compiler是否设置的是JDK1.8

    Appium常用API2:得到当前页面的dom结构(getPageSource)

    可以用于断言当前页面是否有某个元素,或者判断当前页面有没有产生变化,比如上下滚动判断是否已经到了底端或者顶端。

    String pageSource = androidDrivr.getPageSource();
    System.out.println(pageSource);

    Appium常用API3:得到当前页面的类名(currentActivity)

    String currentActivityName = androidDrivr.currentActivity();
    System.out.println(currentActivityName);

    Appium常用API4:重置应用的数据(resetApp)

    有些场景我们需要清除应用的数据,相当于第一次安装时候的状态,比如:第一次启动app的引导页,登录等

    androidDriver.resetApp();

    Appium常用API5:判断App是否安装(isAppInstalled)

    androidDriver.isAppInstalled("应用程序的包名");

    Appium常用API6:向系统发送键值事件(pressKey)

    Android平台独有,向系统发送键值事件,不同的键值对应不同的功能,如keyevent(4)表示手机的HOME按键

    // 1 创建keyEvent对象
    KeyEvent keyEvent = new keyEvent();
    // 2 使用withKey传入键值
    keyEvent.withKey(AndroidKey.VOLUME_UP);
    // 3 使用pressKey发送键值
    androidDriver.pressKey(keyEvent);

    Appium常用API7:截图功能

    使用场景:当测试用例执行失败之后屏幕截图,保存到本地为了更好的查找问题

    File file = androidDriver.getScreenshotAs(OutputType.FILE);
    FileUtils.copyFile(file, new File("D:\AppAuto\test.png"));

    Appium常用API8:获取设备时间信息(getDeviceTime)

    返回String类型

    Appium常用API9:获取设备DPI(getDisplayDensity)

    返回String类型,注意不是分辨率

    Appium常用API10:获取automation name(getAutomationName)

    默认为null,如果有指定automation name为uiautomator2就为对应的值

    Appium常用API11:获取设备横竖屏状态(getOrientation)

    有竖屏(PORTRAIT)和横屏(LANDSCAPE)

    【root状态的Hybrid应用自动化测试介绍】

    Appium提供的解决方案:基于UIAutomator+ChromeDriver

    准备工作:

    1 准备Android 4.4+的手机或者模拟器

    2 在app源码中将webview调试模式打开webview.setWebContentsDebuggingEnabled(true)

    如果是第三方线上app,一般webview debug开关都是关闭的,这就需要借助第三方工具,才能将debug开关打开

    解决方案:(Root状态 )

    Xposed.apk+WebviewDebugHook.apk装入模拟器或者是手机上

    Xposed是一个框架,能够集成很多功能模块,这些模块能够在不修改APK的情况下,修改APP的运行方式。

    安装好Xposed打开显示没有激活,需要在official菜单中选择安装,安装前需要选择适应的框架,如果是模拟器一般是x86框架,如果是真机需要选择arm框架。

    需要WebviewDebugHook模块来开启APP的WebView debug模式 。

    安装并激活好Xposed之后将WebviewDebugHook.apk放入模拟器或者手机,注意此时不会提示安装成功,需要在Xposed中找到“模块”菜单,找到WebviewDebugHook并勾选上,然后重启设备就会生效。

    3 在电脑上安装UC开发者工具(uc-devtools.msi,设置成“本地Devtools Inspector UI资源”)

    UC-Dectools工具的使用需要上面两步配置的支持才能使用,当模拟器或者手机上的当前界面是webView时UC-Dectools工具会检测到,点击UC-Dectools工具上面的“inspect”按钮进入到UI定位界面。

    【Hybrid应用自动化脚本编写】

    # 获取所有的上下文contexts

    driver.getContextHandles();

    # 切换到对应的上下文context

    driver.context(webview视图对应的上下文);

    # 定位webview中的元素,并执行操作

    web网页元素定位和操作,利用Appium提供的解决方案:基于UIAutomator+ChromeDriver,进行webview中元素的定位

    # 切换回默认的视图

    driver.context(native视图);

    示例:

    // 1 进入到web页面中
    androidDriver.findElementByAndroidUIAutomator("new UiSelector().text("网页版")").click();
    // 2 获取所有的contexts
    Set<String> contexts = androidDriver.getContextHandles();
    System.out.printIn(contexts);
    // 3 切换到webview所对应的上下文
    androidDriver.context("WEBVIEW_COM.wuba"); 
    // 4 找到网页中的“价格区间”按钮
    androidDriver.findElementByXPath("//li[@data-id='p25-30']").click();
    // 5 点击网页中的“确认”按钮
    androidDriver.findElementByXPath("//a[text()='确认']").click();

     【注意】在上面的切换到webview所对应的上下文操作中可能会遇上因为ChromeDriver和webView的版本不匹配所导致的错误。简单来说就是,ChromeDriver是appium中的一个工具,app中使用的webview有自己的版本号,ChromeDriver每个版本对webview的可支持版本都不一样,所以需要保证版本的可支持性。

    1 查看ChromeDriver版本号,mac电脑的appium中ChromeDriver所在路径为:

    /Applications/Appium.app/Contents/Resources/app/node_modules/appium/node_modules/appium-chromedriver/chromedriver

    双击会终端运行ChromeDriver会显示出版本号,如下图所示

    2 查看模拟器或者手机的webview版本号,有三种方式:

    1)在模拟器或者真机上打开一款需要webview的app,然后进入到设置-应用程序中打开“显示系统进程”,找到类似下图所示的进程:

    然后点击进去便可查看到版本号:

    2)通过UC-Dectools工具中检测到的webview时,信息上会附带有版本信息。

    3)使用adb工具,首先确保连接上夜神模拟器

    $adb connect 127.0.0.1:62001

    可以通过下面的命令确保是否连接成功:

    $adb devices

    然后查看系统的所有程序:

    $adb shell pm list package -s

    找到webview:

     复制好此包名,然后查看该应用包的信息:

    adb shell dumpsys package com.google.android.webview

    最后找到关于version的相关信息:

    3 第三步对照版本匹配表,在github可以查询到淘宝的镜像网站:

    github上appium【https://github.com/appium/appium/blob/master/docs/en/writing-running-appium/web/chromedriver.md 

    淘宝的镜像网站【http://npm.taobao.org/mirrors/chromedriver

    我电脑上的appium中的ChromeDriver版本号通过上面方式的查询是2.38版本,在镜像网站上找到对应的2.38目录,点击进去长相如下:

     前面三个是安装包,点击最后一个文件“notes.txt”查看该ChromeDriver版本支持哪些webview版本:

    4 如果版本不匹配,替换掉appium中的ChromeDriver应用程序。

    比如本文所示,通过查看ChromeDriver的版本是2.38版本,支持的webview是65-67版本;通过查看webview的版本是75,存在不匹配的情况,因为webview的版本不能更改,因此只能替换掉appium中的ChromeDriver应用程序。

    【非root状态的Hybrid应用自动化测试解决方案】

    解决方案:通过VirtualXposed完成线上App开启webview调试,VirtualXposed相当于一个“应用分身”提供一个虚拟环境,这个虚拟环境中默认装了一个Xposed,

    1 将VirtualXpose.apk安装到真机中,在VirtualXposed中安装WebviewDebugHook.apk和需要测试的app安装包

    2 在VirtualXposed中打开测试的目标app,就相当于在root状态下测试app

  • 相关阅读:
    MySQL中的字符串函数
    用google map实现周边搜索功能
    用 wait-notify 写一段代码来解决生产者-消费者问题
    equals和hashcode为什么要一起重写
    Java知多少(107)几个重要的java数据库访问类和接口
    Java知多少(106)程序与数据库连接
    Java知多少(105)套接字(Socket)
    Java知多少(104)网络编程之统一资源定位符URL
    Java知多少(103)网络编程之IP地址和InetAddress类
    Java知多少(102)多媒体基础
  • 原文地址:https://www.cnblogs.com/cchHers/p/14167636.html
Copyright © 2011-2022 走看看