移动端自动化测试Appium-java

一、Appium的简介

移动端的自动化测试框架

模拟人的操作进行功能自动化常用于功能测试、兼容性测试

跨平台的自动化测试

二、Appium的原理

核心是web服务器,接受客户端的连接,接收客户端的命令,在手机设备上执行命令,收集命令执行的结果。

Session,客户端初始化一个 session 会话,发送 POST/session 请求到服务器端,这些请求里面都会带有一个对象:desiredCapabilities,这个时候服务器端会启动自动化 session 然后返回一个 session ID,以后的命令都会用这个 seesion ID 去匹配。

desired capabilities 对象其实是一个 key-value 的集合,里面包含了各种各样的信息,发送到服务器端后,服务器解析这些信息就知道了客户端对哪种 session 感兴趣,然后就会启动相应的session,这里面的信息会影响着服务器端启动 session 的类型。

Appium Server,Appium 是一个用 Node.js 编写的 HTTP server,它创建、并管理多个 WebDriver sessions 来和不同平台交互。

Apium ClientsAppium 开始一个测试后,就会在被测设备(手机)上启动一个 server ,监听来
自 Appium server 的指令,每种平台(如 iOS 和 Android)都有不同的运行和交互方式,所以 Appium 会用某个桩程序“侵入”该平台,并接受指令,来完成测试用例的运行。

三、Appium的使用

3.1导入依赖

<dependency><groupId>org.testng</groupId><artifactId>testng</artifactId><version>7.0.0-beta1</version></dependency><dependency><groupId>io.appium</groupId><artifactId>java-client</artifactId><version>8.5.1</version><!--            <scope>test</scope>--></dependency><dependency><groupId>org.seleniumhq.selenium</groupId><artifactId>selenium-java</artifactId><version>4.9.1</version></dependency>

3.2 创建测试类

使用@test注解,类的名字要符合标识符命名规则即可


import io.appium.java_client.AppiumDriver;
import org.openqa.selenium.remote.DesiredCapabilities;
import org.testng.annotations.Test;public class NewTest {//定义 AppiumDriver 对象  AppiumDriver driver;@Testpublic void f(){}}

3.3 @BeforeClass 注解

@BeforeClass 注解 用于设定进入测试类后,在所有测试之前首先要执行的代码,指定测试设备平台。

 @BeforeClasspublic void beforeClass() throws MalformedURLException  {//指定测试设备信息DesiredCapabilities device=new  DesiredCapabilities();//使用移动设备或模拟器种类device.setCapability("deviceName","Android Emulator");//指定哪个移动操作平台device.setCapability("platformName", "Android");//指定移动操作系统版本device.setCapability("platformVersion", "4.4.2");//指定 app 程序包名,即被测程序名device.setCapability("appPackage","com.android.calculator2");//指定 app 启动页名称device.setCapability("appActivity",".Calculator");//启动appdriver = new AndroidDriver(new URL("http://localhost:4723/wd/hub"),device);}

3.4  @AfterClass 注解

在所有测试之后再执行的代码,可在该注解写入app退出代码

 @AfterClasspublic void afterClass() {//退出appdriver.quit()}

3.5 @Test(属性) 注解

指定要测试的方法,方法名只要符合标识符命名规则。
属性

description="测试描述"
priority=优先级,从 0 开始
timeout=?ms,超时时间
dataProvider="Dataprovider 的名称或方法名"
dataProviderClass=产生测试数据的类
 

 @Test(description="123",priority=0)public void f() {System.out.println("hello");}

3.6 识别操作元素

通过\Android\android-sdk\tools\ uiautomatorviewer 去探操作元素的id

driver.findElementById(resource-id 属性)

driver.findElementByClassName(class 属性)

  @Test(description="123",priority=0)public void f() {//点击driver.findElementById("com.android.calculator2:id/digit3").click();driver.findElementById("com.android.calculator2:id/plus").click();driver.findElementById("com.android.calculator2:id/digit2").click();driver.findElementById("com.android.calculator2:id/equal").click();//获取文本String sum=driver.findElementByClassName("android.widget.EditText").getText();//输入driver.findElementById("属性").sendKeys("数据");//清空driver.findElementById("属性").clear();System.out.print(sum);}

3.7 断言

断言主要用于判断预期结果与实际结果是否一致

 //用于测试期望结果的断言,即测试两个对象是否相等Assert.assertEquals("实际值", "期望值");//布尔值断言Boolean rs=sum.contains("");Assert.assertTrue(rs);

3.8 模拟按键

driver.pressKeyCode(keycode),必须使用 AmdroidDriver

AndroidDriver driver;@Test
public void f() {driver.pressKeyCode("按键");
}

3.9 参数化

使用数组返回参数

@DataProvider
public Object[][] getData(){Object data[][]={ {"234","+","56","290"},{"256","-","40","216"}, {"3","*","6","18"} };return data;
}@Test(dataProvider="getData")
public void f(String one,String two,String exact) {}

读参数文件

@DataProviderpublic Object[][] getParam() throws Exception{List<String[]> rows=new ArrayList<String[]>();//导入文件File file=new File("文件路径");//读取字节文件FileReader reader=new FileReader(file);//字节转字符BufferedReader buffer=new BufferedReader(reader);String row=null;//读取每一行数据while((row=buffer.readLine())!=null){String columns[]=row.split("\t");rows.add(columns);}//文件关闭reader.close();//转换ObjectObject[][] data=new Object[rows.size()][];for(int i=0;i<rows.size();i++)data[i]=rows.get(i);return data;}

定义获得参数的单独类和方法

使用@DataProvider(name="测试数据集名")修饰获取参数的方法名。
@Test(dataProvider="getParam",dataProviderClass=参数类名.class)

public class Parm {@DataProvider()public Object[][] getParam() throws Exception{Object data[][]={ {"234","+","56","290"},{"256","-","40","216"}, {"3","*","6","18"} };return data;}}

3.10 测试报告

java项目加入TestReport

package app;	//修改为正确的包名
import java.io.File;
import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Paths;
import java.nio.file.StandardOpenOption;
import java.text.SimpleDateFormat;
import java.util.Calendar;
import java.util.Date;
import org.testng.ITestContext;
import org.testng.ITestResult;
import org.testng.TestListenerAdapter;public class TestReport extends TestListenerAdapter{private String reportPath;@Overridepublic void onStart(ITestContext context) {File htmlReportDir = new File("test-output");if (!htmlReportDir.exists()) {htmlReportDir.mkdirs();}String reportName = formateDate()+"_result.html";reportPath = htmlReportDir+"/"+reportName;File report = new File(htmlReportDir,reportName);if(report.exists()){try {report.createNewFile();} catch (IOException e) {e.printStackTrace();}}StringBuilder sb = new StringBuilder("<html><head><meta http-equiv=\"Content-Type\" content=\"text/html; charset=utf-8\" />"+ "<title>自动化测试报告</title></head><body style=\"background-color:#99FFCC;\">"+ "<div id=\"top\" align=\"center\"><p style=\"font-weight:bold;\">测试用例运行结果列表</p>"			+ "<table width=\"90%\" height=\"80\" border=\"1\" align=\"center\" cellspacing=\"0\" rules=\"all\" style=\"table-layout:relative;\">"+ "<thead>"+ "<tr>"+ "<th>测试用例名</th>"+ "<th>测试用例结果</th>"+ "</tr>"+ "</thead>"+ "<tbody style=\"word-wrap:break-word;font-weight:bold;\" align=\"center\">");String res = sb.toString();try {  Files.write((Paths.get(reportPath)),res.getBytes("utf-8"));  } catch (IOException e) {  e.printStackTrace();  } }@Overridepublic void onTestSuccess(ITestResult result) {StringBuilder sb = new StringBuilder("<tr><td>");sb.append(result.getMethod().getRealClass()+"."+result.getMethod().getMethodName());sb.append("</td><td><font color=\"green\">Passed</font></td></tr>");String res = sb.toString();try {Files.write((Paths.get(reportPath)),res.getBytes("utf-8"),StandardOpenOption.APPEND);} catch (IOException e) {e.printStackTrace();}}@Overridepublic void onTestSkipped(ITestResult result) {StringBuilder sb = new StringBuilder("<tr><td>");sb.append(result.getMethod().getRealClass()+"."+result.getMethod().getMethodName());sb.append("</td><td><font color=\"yellow\">Skipped</font>");sb.append("<p align=\"left\">测试用例<font color=\"red\">跳过</font>,原因:<br>");sb.append("<br><a style=\"background-color:#CCCCCC;\">");Throwable throwable = result.getThrowable(); sb.append(throwable.getMessage()); sb.append("</a></p></td></tr>");String res = sb.toString();try {Files.write((Paths.get(reportPath)),res.getBytes("utf-8"),StandardOpenOption.APPEND);} catch (IOException e) {e.printStackTrace();}}@Overridepublic void onTestFailure(ITestResult result) {StringBuilder sb = new StringBuilder("<tr><td>");sb.append(result.getMethod().getRealClass()+"."+result.getMethod().getMethodName());sb.append("</td><td><font color=\"red\">Failed</font><br>");sb.append("<p align=\"left\">测试用例执行<font color=\"red\">失败</font>,原因:<br>");sb.append("<br><a style=\"background-color:#CCCCCC;\">");Throwable throwable = result.getThrowable();sb.append(throwable.getMessage());sb.append("</a></p></td></tr>");String res = sb.toString();try {Files.write((Paths.get(reportPath)),res.getBytes("utf-8"),StandardOpenOption.APPEND);} catch (IOException e) {e.printStackTrace();}}@Overridepublic void onFinish(ITestContext testContext) {StringBuilder sb = new StringBuilder("</tbody></table><a href=\"#top\">返回顶部</a></div></body>");sb.append("</html>");String msg = sb.toString();try {Files.write((Paths.get(reportPath)),msg.getBytes("utf-8"),StandardOpenOption.APPEND);} catch (IOException e) {e.printStackTrace();}}public static String formateDate(){SimpleDateFormat sf = new SimpleDateFormat("yyyyMMdd HHmmss");Calendar cal = Calendar.getInstance();Date date = cal.getTime();return sf.format(date);}
}

测试类中添加监听器@Listeners({TestReport.class})即可,class 是固定关键字,放到测试类名的上一行

@Listeners({TestReport.class})
public class NewTest {}

测试报告默认存储位置Java 项目名\test-output

四、adb命令使用

android debug bridge 即 android 调试桥,在cmd输入

查看版本 adb version

显示所有的设备 adb devices

安装程序  adb install .apk 文件名,apk 文件名即程序包名,程序名中不要使用汉字

卸载 App adb uninstall 完整包名,包名不是 apk 文件名,可以使用uiautomatorviewer寻找package

查看手机操作系统的版本号  adb shell getprop ro.build.version.release

显示设备中的包以及包的启动项 

所有包 adb shell getprop ro.build.version.release

查找某个包 adb shell pm list packages | findstr 查找关键字

查找启动项  adb shell dumpsys window w | findstr \/ | findstr name= 

需要提前启用app

在指定设备中运行命令 adb -s 设备名 shell 命令

adb 服务器的开关 adb start-server adb kill-server

五、

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.rhkb.cn/news/503305.html

如若内容造成侵权/违法违规/事实不符,请联系长河编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

Geoserver修行记-后端调用WMS/WMTS服务无找不到图层Could not find layer

项目场景 调用geoserver地图服务WMS,找不到图层 我在进行地图服务调用的时候&#xff0c;总是提示我找不多图层 Could not find layer&#xff0c;重点是这个图层我明明是定义了&#xff0c;发布了&#xff0c;且还能够正常查看图层的wms的样式&#xff0c;但是在调用后端调用…

深入探讨 Android 中的 AlarmManager:定时任务调度及优化实践

引言 在 Android 开发中&#xff0c;AlarmManager 是一个非常重要的系统服务&#xff0c;用于设置定时任务或者周期性任务。无论是设置一个闹钟&#xff0c;还是定时进行数据同步&#xff0c;AlarmManager 都是不可或缺的工具之一。然而&#xff0c;随着 Android 系统的不断演…

玉米识别数据集,4880张图,正确识别率可达98.6%,支持yolo,coco json,pasical voc xml格式的标注,可识别玉米

玉米识别数据集&#xff0c;4880张图&#xff0c;正确识别率可达98.6%&#xff0c;支持yolo&#xff0c;coco json,pasical voc xml格式的标注&#xff0c;可识别玉米 数据集下载地址&#xff1a; yolo v11:https://download.csdn.net/download/pbymw8iwm/90230969 yolo v9:…

【UI自动化测试】selenium八种定位方式

&#x1f3e1;个人主页&#xff1a;謬熙&#xff0c;欢迎各位大佬到访❤️❤️❤️~ &#x1f472;个人简介&#xff1a;本人编程小白&#xff0c;正在学习互联网求职知识…… 如果您觉得本文对您有帮助的话&#xff0c;记得点赞&#x1f44d;、收藏⭐️、评论&#x1f4ac;&am…

【前端系列01】优化axios响应拦截器

文章目录 一、前言&#x1f680;&#x1f680;&#x1f680;二、axios响应拦截器&#xff1a;☀️☀️☀️2.1 为什么前端需要响应拦截器element ui的消息组件 一、前言&#x1f680;&#x1f680;&#x1f680; ☀️ 回报不在行动之后&#xff0c;回报在行动之中。 这个系列可…

【C语言程序设计——选择结构程序设计】求阶跃函数的值(头歌实践教学平台习题)【合集】

目录&#x1f60b; 任务描述 相关知识 1. 选择结构基本概念 2. 主要语句类型​&#xff08;if、if-else、switch&#xff09; 3. 跃迁函数中变量的取值范围 4. 计算阶跃函数的值 编程要求 测试说明 通关代码 测试结果 任务描述 本关任务&#xff1a;输入x的值&#x…

利用 NineData 实现 PostgreSQL 到 Kafka 的高效数据同步

记录一次 PostgreSQL 到 Kafka 的数据迁移实践。前段时间&#xff0c;NineData 的某个客户在一个项目中需要将 PostgreSQL 的数据实时同步到 Kafka。需求明确且普遍&#xff1a; PostgreSQL 中的交易数据&#xff0c;需要实时推送到 Kafka&#xff0c;供下游多个系统消费&#…

【C++面向对象——类的多态性与虚函数】编写教学游戏:认识动物(头歌实践教学平台习题)【合集】

目录&#x1f60b; 任务描述 详细说明&#xff08;类的设计&#xff09; 基类&#xff1a; Animal 派生类: 应用程序说明&#xff1a; 相关知识 1. 虚函数与多态 一、多态的概念与意义 二、虚函数实现多态的原理 三、虚函数的语法细节 2. 纯虚函数与抽象类 一、纯虚…

我的nvim的init.lua配置

nvim的配置文件路径在&#xff5e;/.config/nvim路径下&#xff1a; 一、目录如下&#xff1a; coc-settings.json文件是配置代码片段路径的文件init.lua配置文件的启动脚本lua/config.lua 全局配置文件lua/keymaps.lua 快捷键映射键文件lua/plugins.lua 插件的安装和配置文件…

微服务-Eureka

Eureka的作用 使用RestTemplate完成远程调用需要被调用者的ip和端口&#xff0c;从而能够发起http请求&#xff0c;但是如果有很多个实例也更加不能有效的处理&#xff0c;而且我们又该如何知道这些实例是否健康呢。所以就有了很多的注册中心比如Eureka、Nacos等等。 服务注…

微服务保护—Sentinel快速入门+微服务整合 示例: 黑马商城

1.微服务保护 微服务保护是确保微服务架构可靠、稳定和安全的策略与技术。 在可靠性上&#xff0c;限流是控制进入微服务的请求数量&#xff0c;防止流量过大导致服务崩溃。比如电商促销时对商品详情服务进行流量限制。熔断是当被调用的微服务故障过多或响应过慢时&#xff0c;…

Maven 详细配置:Maven settings 配置文件的详细说明

Maven settings 配置文件是 Maven 环境的重要组成部分&#xff0c;它用于定义用户特定的配置信息和全局设置&#xff0c;例如本地仓库路径、远程仓库镜像、代理服务器以及认证信息等。settings 文件分为全局配置文件&#xff08;settings.xml&#xff09;和用户配置文件&#x…

【Uniapp-Vue3】image媒体组件属性

如果我们想要在页面上展示图片就需要使用到image标签。 这部分最重要的是图片的裁剪&#xff0c;图片的裁剪和缩放属性&#xff1a; mode 图片裁剪、缩放的模式 默认值是scaleToFill 我将用两张图片对属性进行演示&#xff0c;一张是pic1.jpg&#xff08;宽更长&#xf…

http源码分析

一、HttpURLConnection http连接池源码分析 二、HttpClient 连接池&#xff0c;每个路由最大连接数 三、OkHttp okhttp的连接池与socket连接

接口开发完后,个人对于接下来接口优化的一些思考

优化点 入参的合法性和长度范围&#xff0c;必填项的检查验证 因为没有入参&#xff0c;所以不需要考虑。 批量思想解决N1问题 // 假设要查询100个订单及其对应的用户信息 List<Order> orders orderMapper.selectList(new QueryWrapper<>().last("limit …

运动相机拍摄的视频打不开怎么办

3-10 GoPro和大疆DJI运动相机的特点&#xff0c;小巧、高清、续航长、拍摄稳定&#xff0c;很多人会在一些重要场合用来拍摄视频&#xff0c;比如可以用来拿在手里拍摄快速运动中的人等等。 但是毕竟是电子产品&#xff0c;有时候是会出点问题的&#xff0c;比如意外断电、摔重…

STM32-BKP备份寄存器RTC实时时钟

一、原理 Unix&#xff1a; 一些系统是使用32bit有符号数存储&#xff0c;实际范围为-2,147,483,648到2,147,483,647‌即~ 经过计算int32数据会在2038年1月19日溢出&#xff0c;可以看到转换的为北京时间。 STM32的时间戳为无符号时间戳。 我们需要把秒计数器的时间通过计算…

Docker图形化界面工具Portainer最佳实践

前言 安装Portainer 实践-基于Portainer安装redis-sentinel部署 Spring Boot集成Redis Sentinel 前言 本篇文章笔者推荐一个笔者最常用的docker图形化管理工具——Portainer。 安装Portainer 编写docker-compose文件 Portainer部署的步骤比较简单&#xff0c;我们还是以…

行业商机信息付费小程序系统开发方案

行业商机信息付费小程序系统&#xff0c;主要是整合优质行业资源&#xff0c;实时更新的商机信息。在当今信息爆炸的时代&#xff0c;精准、高效地获取行业商机信息对于企业和个人创业者而言至关重要。 一、使用场景 日常浏览&#xff1a;用户在工作间隙或闲暇时间&#xff0c…

基于Matlab的变压器仿真模型建模方法(13):单相升压自耦变压器的等效电路和仿真模型

1.单相升压自耦变压器的基本方程和等效电路 单相升压自耦变压器的接线原理图如图1所示。在建立自耦变压器的基本方程时,仍然把它看成是从双绕组变压器演变而来。在图1中,设节点a到节点b部分的绕组的匝数为,对应于双绕组变压器的原边绕组;节点c到节点a部分的绕组的绕组匝数为…