当前位置: 首页 > news >正文

移动端自动化测试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

五、

相关文章:

移动端自动化测试Appium-java

一、Appium的简介 移动端的自动化测试框架 模拟人的操作进行功能自动化常用于功能测试、兼容性测试 跨平台的自动化测试 二、Appium的原理 核心是web服务器&#xff0c;接受客户端的连接&#xff0c;接收客户端的命令&#xff0c;在手机设备上执行命令&#xff0c;收集命令…...

IO: 作业:Day1

思维导图 main.c #include"student.h" int main(int argc, const char *argv[]) { stuPtr hcreat(); int n0; add_node(h); add_node(h); add_node(h); show(h); save(h,"student.txt"); stuPtr ptrc…...

ue5 替换角色的骨骼网格体和动画蓝图

一开始动画蓝图&#xff0c;骨骼网格体都是用的女性角色 现在把它换成男性 编译 保存 运行 把动画类换成ABP_Manny 进入ABP_Manny中 进入到idle 找到这个拖进来 编译 就变成站着端枪 运行一下&#xff0c;没有问题...

el-cascader 树状选择-点击父级禁用子级

背景&#xff1a;项目上需要实现树状选择&#xff0c;点击父级禁用子级的功能&#xff0c;element组件本身没有该配置项说明&#xff1a;需要实现几个功能点&#xff1a;点击父级禁用子级&#xff1b;再次点击取消禁用&#xff1b;仅回填所选级&#xff1b;上下级不关联实现代码…...

AWS re:Invent 的创新技术

本月早些时候&#xff0c;Amazon 于 12 月 1 日至 5 日在内华达州拉斯维加斯举行了为期 5 天的 re&#xff1a;Invent 大会。如果您从未参加过 re&#xff1a;Invent 会议&#xff0c;那么最能描述它的词是“巨大”——不仅从与会者人数&#xff08;60,000 人&#xff09;来看&…...

PHP7和PHP8的最佳实践

php 7 和 php 8 的最佳实践包括&#xff1a;使用类型提示以避免运行时错误&#xff1b;利用命名空间组织代码并避免命名冲突&#xff1b;采用命名参数、联合类型等新特性增强可读性&#xff1b;用错误处理优雅地处理异常&#xff1b;关注性能优化&#xff0c;如避免全局变量和选…...

Debian、Ubuntu 22.04和ubuntu 24.04国内镜像源(包括 docker 源)

Debian 更换国内清华源 1、备份原文件mv /etc/apt/sources.list /etc/apt/sources.list.old 2、写入新源&#xff0c;以下是 Debian 11 的&#xff1a; cat > /etc/apt/sources.list << EOF deb https://mirrors.tuna.tsinghua.edu.cn/debian/ bullseye main contrib…...

点亮一个esp32 的led

最近入了一个ESP32 兄弟们&#xff0c;这玩意还可以&#xff0c;买来肯定是给它点亮啊对吧 我就是点灯侠&#x1f387; &#x1f62d;千万不要不接天线啊&#xff0c;不然你会一直找不到你的wifi 1.点灯第一步你得有IDE Arduino 就是这个绿东西 可是怎么下载安装呢&#xff…...

C++ shared_ptr进一步认知,为什么引用计数>2退出作用域都可以调用析构

1.使用智能指针需要#include <memeroy> 2.上代码&#xff1a; #include <memory> #include <iostream> using namespace std; struct lifePeriod {lifePeriod():a(1){cout << "无参构造&#xff01;" << endl;}virtual ~lifePeriod(…...

JavaScript代码片段二

见过不少人、经过不少事、也吃过不少苦&#xff0c;感悟世事无常、人心多变&#xff0c;靠着回忆将往事串珠成链&#xff0c;聊聊感情、谈谈发展&#xff0c;我慢慢写、你一点一点看...... JavaScript统计文字个数、特殊字符转义、动态插入js代码、身份证验证 统计文字个数 f…...

【计算机视觉】单目深度估计模型-Depth Anything-V2

概述 本篇将简单介绍Depth Anything V2单目深度估计模型&#xff0c;该模型旨在解决现有的深度估计模型在处理复杂场景、透明或反射物体时的性能限制。与前一代模型相比&#xff0c;V2版本通过采用合成图像训练、增加教师模型容量&#xff0c;并利用大规模伪标签现实数据进行学…...

Servlet 和 Spring MVC:区别与联系

前言 在 Java Web 开发中&#xff0c;Servlet 和 Spring MVC 是两个重要的技术。Servlet 是 Java Web 的基础组件&#xff0c;而 Spring MVC 是一个高级 Web 框架&#xff0c;建立在 Servlet 的基础之上&#xff0c;提供了强大的功能和易用性。这篇文章将从定义、原理、功能对…...

【期末复习】三、内存管理

1.物理内存管理 空闲内存管理方式主要分为:等长划分和不等长划分。 内存管理方式 单一连续分区 基本思想:一段时间内只有一个进程在内存。 特点:简单,内存利用率低, 有三种不同的布局: 固定分区 把内存空间分割成若干区域, 称为分区。 每个分区的大小可以相同也可…...

Microsoft Azure Cosmos DB:全球分布式、多模型数据库服务

目录 前言1. Azure Cosmos DB 简介1.1 什么是 Azure Cosmos DB&#xff1f;1.2 核心技术特点 2. 数据模型与 API 支持2.1 文档存储&#xff08;Document Store&#xff09;2.2 图数据库&#xff08;Graph DBMS&#xff09;2.3 键值存储&#xff08;Key-Value Store&#xff09;…...

【Docker】安装registry本地镜像库,开启Https功能

下载镜像 docker pull registry:2 需要启动https功能&#xff0c;就要生成服务端的自签名的证书和私钥&#xff0c;以及在docker客户端安装这个经过签名的证书。 第一步&#xff1a;生成公私钥信息&#xff0c;第二步&#xff0c;制作证书签名申请文件&#xff0c; 第三步&…...

JUC--线程池

线程池 七、线程池7.1线程池的概述7.2线程池的构建与参数ThreadPoolExecutor 的构造方法核心参数线程池的工作原理 Executors构造方法newFixedThreadPoolnewCachedThreadPoolnewSingleThreadExecutornewScheduledThreadPool(int corePoolSize) 为什么不推荐使用内置线程池&…...

后端Java开发:第十一天

第十一天&#xff1a;方法重载 - 理解与应用 今天我们继续深入 Java 的世界&#xff0c;讨论 Java 中的 方法重载&#xff08;Method Overloading&#xff09;。你可能会想&#xff0c;什么是方法重载&#xff1f;简单来说&#xff0c;方法重载允许你在一个类中定义多个同名方…...

基于 GEE 的长时间序列 Landsat 5 影像下载

目录 1 完整代码 2 运行结果 1 完整代码 var LT5 ee.ImageCollection("LANDSAT/LT05/C01/T1"),imageVisParam {"opacity":1,"bands":["B4","B3","B2"],"gamma":1},roi ee.FeatureCollection(&quo…...

Unity-Mirror网络框架从入门到精通之Attributes属性介绍

前言 在现代游戏开发中&#xff0c;网络功能日益成为提升游戏体验的关键组成部分。Mirror是一个用于Unity的开源网络框架&#xff0c;专为多人游戏开发设计。它使得开发者能够轻松实现网络连接、数据同步和游戏状态管理。本文将深入介绍Mirror的基本概念、如何与其他网络框架进…...

软考证书邮寄步骤

一、点击网址 https://www.ruankao.org.cn/ 复制上述网址&#xff0c;粘贴至浏览器中。点击 “报名入口” 。 二、点击入口 选择考试批次。点击你所在考试地点的入口并进入。 三、登录 输入手机号和密码。进行验证。 四、点击基本信息 点击右上角。进入 “基本信息” 。 五、…...

Vim 调用外部命令学习笔记

Vim 外部命令集成完全指南 文章目录 Vim 外部命令集成完全指南核心概念理解命令语法解析语法对比 常用外部命令详解文本排序与去重文本筛选与搜索高级 grep 搜索技巧文本替换与编辑字符处理高级文本处理编程语言处理其他实用命令 范围操作示例指定行范围处理复合命令示例 实用技…...

深度学习在微纳光子学中的应用

深度学习在微纳光子学中的主要应用方向 深度学习与微纳光子学的结合主要集中在以下几个方向&#xff1a; 逆向设计 通过神经网络快速预测微纳结构的光学响应&#xff0c;替代传统耗时的数值模拟方法。例如设计超表面、光子晶体等结构。 特征提取与优化 从复杂的光学数据中自…...

conda相比python好处

Conda 作为 Python 的环境和包管理工具&#xff0c;相比原生 Python 生态&#xff08;如 pip 虚拟环境&#xff09;有许多独特优势&#xff0c;尤其在多项目管理、依赖处理和跨平台兼容性等方面表现更优。以下是 Conda 的核心好处&#xff1a; 一、一站式环境管理&#xff1a…...

【Python】 -- 趣味代码 - 小恐龙游戏

文章目录 文章目录 00 小恐龙游戏程序设计框架代码结构和功能游戏流程总结01 小恐龙游戏程序设计02 百度网盘地址00 小恐龙游戏程序设计框架 这段代码是一个基于 Pygame 的简易跑酷游戏的完整实现,玩家控制一个角色(龙)躲避障碍物(仙人掌和乌鸦)。以下是代码的详细介绍:…...

MySQL 隔离级别:脏读、幻读及不可重复读的原理与示例

一、MySQL 隔离级别 MySQL 提供了四种隔离级别,用于控制事务之间的并发访问以及数据的可见性,不同隔离级别对脏读、幻读、不可重复读这几种并发数据问题有着不同的处理方式,具体如下: 隔离级别脏读不可重复读幻读性能特点及锁机制读未提交(READ UNCOMMITTED)允许出现允许…...

UE5 学习系列(三)创建和移动物体

这篇博客是该系列的第三篇&#xff0c;是在之前两篇博客的基础上展开&#xff0c;主要介绍如何在操作界面中创建和拖动物体&#xff0c;这篇博客跟随的视频链接如下&#xff1a; B 站视频&#xff1a;s03-创建和移动物体 如果你不打算开之前的博客并且对UE5 比较熟的话按照以…...

最新SpringBoot+SpringCloud+Nacos微服务框架分享

文章目录 前言一、服务规划二、架构核心1.cloud的pom2.gateway的异常handler3.gateway的filter4、admin的pom5、admin的登录核心 三、code-helper分享总结 前言 最近有个活蛮赶的&#xff0c;根据Excel列的需求预估的工时直接打骨折&#xff0c;不要问我为什么&#xff0c;主要…...

2025 后端自学UNIAPP【项目实战:旅游项目】6、我的收藏页面

代码框架视图 1、先添加一个获取收藏景点的列表请求 【在文件my_api.js文件中添加】 // 引入公共的请求封装 import http from ./my_http.js// 登录接口&#xff08;适配服务端返回 Token&#xff09; export const login async (code, avatar) > {const res await http…...

AI书签管理工具开发全记录(十九):嵌入资源处理

1.前言 &#x1f4dd; 在上一篇文章中&#xff0c;我们完成了书签的导入导出功能。本篇文章我们研究如何处理嵌入资源&#xff0c;方便后续将资源打包到一个可执行文件中。 2.embed介绍 &#x1f3af; Go 1.16 引入了革命性的 embed 包&#xff0c;彻底改变了静态资源管理的…...

sipsak:SIP瑞士军刀!全参数详细教程!Kali Linux教程!

简介 sipsak 是一个面向会话初始协议 (SIP) 应用程序开发人员和管理员的小型命令行工具。它可以用于对 SIP 应用程序和设备进行一些简单的测试。 sipsak 是一款 SIP 压力和诊断实用程序。它通过 sip-uri 向服务器发送 SIP 请求&#xff0c;并检查收到的响应。它以以下模式之一…...