一、使用Android工具查找包名和activity
以下有三种查看应用包名package和入口activity名称的方法:
方法一:使用aapt //aapt是sdk自带的一个工具,在sdk\builds-tools\目录下
1.以ES文件浏览器为例,命令行中切换到aapt.exe目录执行:aapt dump badging E:\apk\es3.apk | findstr "package launchable-activity"
2.运行后的结果中以下两行分别是应用包名package和入口activity名称
package: name=’com.estrongs.android.pop’
launchable-activity: name=’com.estrongs.android.pop.view.FileExplorerActivity’
注:在android sdk目录搜索可以找到aapt.exe,如果没有可以下载apktool。
查看当前的activity:
adb shell dumpsys window windows|findstr “mCurrentFocus”
方法二:查看AndroidManifest.xml
1.使用apktool反编译app:apktool.bat d es3.apk E:\apk\es
2.打开AndroidManifest.xml
manifest节点的package属性值是应用的包名:<manifest package=”com.estrongs.android.pop”>
查找android.intent.action.MAIN和android.intent.category.LAUNCHER对应的activity,该activity对应的android:name属性既是入口activity名称,如下:
<activity android:theme=”@*android tyle/Theme.NoTitleBar” android:label=”@string/app_name” android:name=”com.estrongs.android.pop.view.FileExplorerActivity”>
<intent-filter>
<action android:name=”android.intent.action.MAIN” />
<category android:name=”android.intent.category.LAUNCHER” />
</intent-filter>
</activity>
android.intent.action.MAIN决定应用程序最先启动的Activity
android.intent.category.LAUNCHER决定应用程序是否显示在程序列表里
方法三:通过hierarchyviewer.bat查看包名和activity
比如在设备上打开一个计算器,然后进入adt目录的sdk\tools中打开hierarchyviewer.bat
D:\adt-bundle-windows-x86_64-20140702\sdk\tools\hierarchyviewer.bat
就能看到包名和activity
注:也可以问Android开发包名和activity,执行appium自动化测试安装app时在appium图形化视图中看日志也可以查看到包名和activity。
二、使用Android工具查找元素
进入sdk\tools\uiautomatorview.bat 打开这个工具
monitor.bat和hierarchyviewer.bat工具也能查看元素,只能看到元素的坐标,没有uiautomatorview.bat好用。
三、扩展学习adb命令
参考:https://www.jianshu.com/p/fb4462919015
四、appium服务关键字
1.deviceName
手机设备的名字或者模拟器名字(虽然是必须项,但在安卓平台上这个值写啥都行,不影响脚本)
2.app
App(.ipa/.apk)安装文件所在路径,本地路径或者远程路径,或者是一个包含app安装文件的.zip文件。Appium将先尝试安装应用到设备上。注意,如果在安卓上你指定了应用的appPackage和appActivity那么app这个参数就不是必须的。这个参数与browserName冲突,例如:/abs/path/to/my.apk or http://myapp.com/app.ipa
3.browserName
手机web自动化的浏览器的名字。如果是做一个app的自动化这个参数值为空(但是实际上这个参数直接不写就可以了)
例如:'Safari' for iOS and 'Chrome', 'Chromium', or 'Browser' for Android
4.newCommandTimeout
等待新命令超时时间,单位是秒,默认是60秒。当appium服务在指定的超时时间内未接收到任何来自客户端的指令,appium服务端与客户端会主动断开链接
一般可以设置时间为600秒,即10分钟,这样不影响自动化在执行过程中等待元素的加载。
5.autoLaunch
设置appiun启动后是否自动安装和启动app应用。默认值是true(当这个值是true时,代码里则不能使用driver.launchApp方法)
6.udid
链接物理指定设备的唯一标识(安卓上就是通过adb devices看到的设备名称)
7.noReset
不重置应用数据,默认是false
8.fullReset
(iOS) 删除整个虚拟目录,(Android) 通过卸载app来重置应用默认是false
9.appActivity
应用的起始启动
10.appPackage
应用的包名(当以安装模式执行脚本的时候,appPackage和appActivity可以不写)
11.appWaitActivity
启动后等待的activity
12.deviceReadyTimeout
等待设备连接正常的超时时间
13.unicodeKeyboard
使用Unicode input输入法, true
14.resetKeyboard
在运行了unicodeKeyboard完成测试后将输入法重置为原有状态,如果单独使用该参数将被忽略,true(该参数好像没用,测试前需要把Android的输入法改成appium Android input manager for unicode,测试时才可以输入中文。如果测试前没有修改为这个输入法,测试时中文输入会失败)
15.noSign
在uiautomator模式下跳过检查和应用的重签名, true
在Androidsetting方法中会用到上面讲到的关键字,代码如下:
public void AndroidSettings() throws MalformedURLException {
//获取当前根目录下的文件,当前根目录就是D:\Springboot\AppiumTest
File classpathRoot = new File(System.getProperty("user.dir"));
//获取apps目录下的文件
File appDir = new File(classpathRoot, "/apps");
//需要提前把该apk放到apps文件夹下
File app = new File(appDir, "meituan-9.12.801.apk");
//创建设备属性对象
DesiredCapabilities capabilities = new DesiredCapabilities();
//设置设备系统为Android
capabilities.setCapability("platformName", "Android");
//设置设备名称为edaed87d,可以通过usb连接手机,在cmd中输入命令,adb devices 看到设备名称
// (这个名称写什么都行,通常情况下测试时只会连一台设备,appium会直接去连,如果有多台用udid参数指定设备号)
capabilities.setCapability("deviceName","edaed87d");
//udid指定设备信息,如果有多台设备这个值会有用
// capabilities.setCapability("udid","edaed87d");
//设置设备安卓系统,可以在手机的设置中查看系统号
capabilities.setCapability("platformVersion", "6.0.1");
//获取app的绝对路径
// capabilities.setCapability("app", app.getAbsolutePath());
//设置app的包名,在cmd中进入aapt.exe的目录输入命令aapt dump badging E:\apk\es3.apk | findstr "package launchable-activity" 查看包名和activity
capabilities.setCapability("appPackage", "com.sankuai.meituan");
//设置app的入口activity
capabilities.setCapability("appActivity", "com.meituan.android.pt.homepage.activity.MainActivity");
//设置每次运行不清空app的数据
capabilities.setCapability("noReset","true");
// //设置每次运行不重签名app
// capabilities.setCapability("noSign","true");
//设置超时时间,超过10分钟,客户端与服务器断开链接
// capabilities.setCapability("newCommandTimeout",600);
//在Android4.2版本以下需要指定这个Selendroid
// capabilities.setCapability("automationName","Selendroid");
//使用Unicode编码方式发送字符串
capabilities.setCapability("unicodeKeyboard","True");
//恢复键盘输入法,也可以说是隐藏键盘(要输入中文,需要先把手机上的输入法调整成appium android input manager for unicode)
//如果需要把键盘调出来只能手工的去改输入法
capabilities.setCapability("resetKeyboard","True");
//设置测试机与idea的通讯协议
driver = new AndroidDriver<WebElement>(new URL("http://127.0.0.1:4723/wd/hub"), capabilities);
driver.manage().timeouts().implicitlyWait(15, TimeUnit.SECONDS);
System.out.println("App is launched!");
}
五、定位
5.1 Xpath定位:
Xpath定位中都是用classname定位的。
WebElement element = driver.findElement(By.xpath("//android.support.v7.widget.RecyclerView/child::android.view.ViewGroup[1]/android.widget.FrameLayout"));
element.click();
要定位到红框里的元素,首先找到它的父级目录,父级目录的classname是android.support.v7.widget.RecyclerView
红框里的元素的classname是android.view.ViewGroup,他是第1个元素,所以
写android.view.ViewGroup[1],如果是最后一个就可以写[last()],如果要定位倒数第2个就写[last()-1]
android.view.ViewGroup[1]下面还有元素,我们要定位到以上图中的红框的元素,找到该元素的classname是android.widget.FrameLayout
该xpath中用到一个child:: 就是存在一个父级和子级的关系:
driver.findElement(By.xpath("//android.support.v7.widget.RecyclerView/child::android.view.ViewGroup[1]/android.widget.FrameLayout"));
如果父级跟目录下有多个级别元素,可以通过descendant:: 定位子孙元素:
通过父级去找:
driver.findElement(By.xpath("//android.widget.FrameLayout{contains(@resource-id,'tv.danmaku.bili:id/cover_layout')}/parent::android.view.ViewGroup/fowlling:sibling::android.view.ViewGroup[2]/android.widget.FrameLayout/android.widget.ImageView")).click();
通过parent找到上一级,然后再找同一目录下的其他元素。
一般通过定位classname和id就能定位到元素,Xpath会有点繁琐,用findElementbyId和findElementbyClassname实现上面的定位
代码如下:
//找到第一个元素点击,默认是第一个元素
// driver.findElement(By.id("tv.danmaku.bili:id/cover")).click();
//找到第二个元素点击,因为id都一样,所有通过list来接收,通过elements.get(1).click();点击第二个元素
List<AndroidElement> elements = driver.findElementsById("tv.danmaku.bili:id/cover");
elements.get(1).click();
//点击屏幕
driver.findElementById("tv.danmaku.bili:id/video_view_biliplayer").click();
//点击右上角返回键
driver.findElementByClassName("android.widget.ImageButton").click();
以上代码要定位到第二个视频,打开后再返回
xpath内容补充:
一、XPath
from lxml import etree
html = etree.parse('html源代码',etree.HTMLPaser())
1.节点的获取
a.html.xpath('//*') #匹配HTML中的所有节点
b.html.xapth('//li') #匹配所有的li节点
c.// 获取所有的子孙节点 ‘/’ 获取子节点(区别好 '//' 与 '/')
d.html.xapth('//li[@class='item-0']') #根据属性匹配
e.html.xapth('//li[contains(@class,'li')]) #属性多值匹配 contains()方法,第一个参数传入属性名称,第二个参数传入属性值,只要此属性包含所传入的属性值就可以完成匹配
f.多属性匹配 .html.xapth('//li[contains(@class,'li') and @name='item'])
g.按顺序选择 html.xapth('//li[last()]') 最后一个li节点
html.xapth('//li[position()<3]') 位置小于3的li节点
html.xapth('//li[1]') 选择第一个li节点
h.节点轴选择 html.xapth('//li/child::') 直接子节点
html.xapth('//li/ancestor::') 所有祖先节点
html.xapth('//li'/attribute::) 获取所有的属性值
html.xapth('//li/decendant::') 获取所有的子孙节点
2.属性,文本获取
(1).文本获取
html.xapth('//li/text()')
(2).属性获取
html.xapth('//li/@class') #获取li标签的class的属性值
5.2 滑动操作
Appium处理滑动方法是swipe
滑动API:Swipe(int start x,int start y,int end x,int y,duration)
解释:
int start x-开始滑动的x坐标;
int start y -开始滑动的y坐标 ;
int end x -结束点x坐标;
int end y -结束点y坐标;
duration 滑动时间(默认5毫秒)。
屏幕左上角为起点,坐标为(0,0),起点往右为Y轴,起点以下为X轴
微信来说,登录按钮起点和终点坐标分别为(63,1716)和(432,1860)
封装的代码:
package com.yin.Appiumdemo;
import io.appium.java_client.AppiumDriver;
import org.openqa.selenium.WebElement;
public class SwipeCenter {
/*
方法参数介绍:
driver(就是把appiumdriver对象传进来)
during(这里是填写毫秒数,这里的 毫秒数越小 滑动的速度越快~ 一般设定在500~1000,如果你想快速滑动 那就可以设置的更加小)
num(是只滑动的次数,如相册多张图片翻页测试什么的滑动或者滑动到列表底部。就直接输入次数就行了)
*/
//上滑 swipeToUp
public static void swipeToUp(AppiumDriver driver,int during,int num) throws InterruptedException {
//先获取屏幕的分辨率
int width = driver.manage().window().getSize().width;
int height = driver.manage().window().getSize().height;
Thread.sleep(2000);
for (int i=0;i<num;i++){
driver.swipe(width/2,height*3/4,width/2,height/4,during);
Thread.sleep(2000);
}
}
//下拉 swipeToDown
public static void swipeToDown(AppiumDriver<WebElement> driver,int during,int num) throws InterruptedException {
//先获取屏幕的分辨率
int width = driver.manage().window().getSize().width;
int height = driver.manage().window().getSize().height;
//等待2秒
Thread.sleep(2000);
for (int i=0;i<num;i++){
driver.swipe(width/2,height/4,width/2,height*3/4,during);
//等待2秒
Thread.sleep(2000);
}
}
//向左滑动 swipeToLeft
public static void swipeToLeft(AppiumDriver<WebElement> driver,int during,int num) throws InterruptedException {
//先获取屏幕的分辨率
int width = driver.manage().window().getSize().width;
int height = driver.manage().window().getSize().height;
//等待2秒
Thread.sleep(2000);
for (int i=0;i<num;i++){
driver.swipe(width*3/4,height/2,width/4,height/2,during);
//等待2秒查看界面变动
Thread.sleep(2000);
}
}
//向右滑动 swipeToRight
public static void swipeToRight(AppiumDriver<WebElement> driver,int during,int num) throws InterruptedException {
//先获取屏幕的分辨率
int width = driver.manage().window().getSize().width;
int height = driver.manage().window().getSize().height;
//等待2秒
Thread.sleep(2000);
for (int i=0;i<num;i++){
driver.swipe(width/4,height/2,width*3/4,height/2,during);
//等待2秒查看界面变动
Thread.sleep(2000);
}
}
}
调用的代码:
@Test(description = "滑动操作")
public void testswip() throws InterruptedException {
//调取SwipeCenter类中的方法,实现向上、向下、向左、向右滑动
//向上滑动
SwipeCenter.swipeToUp(driver,500,2);
//向下滑动
SwipeCenter.swipeToDown(driver,500,1);
//向左滑动
SwipeCenter.swipeToLeft(driver,500,1);
//向右滑动
SwipeCenter.swipeToRight(driver,500,1);
}
还可以利用try、catch,先去找元素,找不到滑动页面再找
WebElement element = null;
try {
//先去找元素
element = driver.findElementByName("百万播放");
}catch(Exception e){
e.printStackTrace();
//找不到向左滑动
SwipeCenter.swipeToLeft(driver,500,1);
element = driver.findElementByName("百万播放");
//找到后再点击click
element.click();
}
5.3 location坐标定位
@Test(description = "location坐标定位")
public void testlocation() throws InterruptedException {
//tap模拟手指点击
//先等待2秒,等待元素出来
Thread.sleep(2000);
//使用tap方法,第一个参数是模拟一个手指去点,第二个参数是X点,第三个是y点,第四个是持续点击0.5秒
//通过Ui Automator viewer定位看到坐标点是[846,362][1080,482],取X值在846~1080之间,取Y值在362~482之间
driver.tap(1,900,420,500);
Thread.sleep(2000);
System.out.println("坐标定位成功");
}
5.4 混合应用定位获取webview页面的元素
混合应用就是在app中嵌套了H5的页面,H5页面的元素是webview的。
步骤都在代码中有些注释:
@Test(description = "获取webview页面元素")
public void testwebview(){
//获取driver中的handles,利用foreach循环查找如果是webview的页面就进入webview页面,转换成html在浏览器中打开再去定位到某个元素
//定位成功之后再转回到NATIVE_APP 页面
Set<String> contextset = driver.getContextHandles();
for (String context : contextset ){
System.out.println("页面上context是:"+context);
if (context.toLowerCase().contains("webview")){
driver.context(context);//进入webview页面
System.out.println("进入成功");
break;
}
}
//通过封装的方法把webview源码转换成html,然后在浏览器中打开去定位到元素
//封装的方法还没有实现,如果找到webview页面,获取到源码使用chrome-development tool 来定位元素
//使用步骤参考:https://blog.csdn.net/wanglin_lin/article/details/77100028
// PageResourceGetter.initialize("uers/dir/prg.html",driver.getPageSource());
//定位到元素
driver.findElement(By.id("username")).sendKeys("appium.test@gmail.com");
//再回到原生app
driver.switchTo().window("NATIVE_APP");
}
参考地址:https://blog.csdn.net/wanglin_lin/article/details/77100028
一般情况下通过Ui Automator viewer也可以看到webview中的控件,如果看不到就把driver.getPageSource()的内容打印出来,然后分析里面的控件再定位也可以。
appium的api
手机坐标、首页导航滑动前面已经讲过,省略。
5.5 截屏操作
先创建一个截屏类:
package com.yin.Appiumdemo;
/*
截图操作
*/
import io.appium.java_client.AppiumDriver;
import io.appium.java_client.android.AndroidDriver;
import org.apache.commons.io.FileUtils;
import org.openqa.selenium.OutputType;
import java.io.File;
import java.io.IOException;
import java.text.SimpleDateFormat;
import java.util.Date;
public class Screenshot {
public static void Screenshot(AppiumDriver driver, String ScreenName) throws IOException {
//设置时间格式,截图的文件要加上时间戳这样不会重复
SimpleDateFormat simpleDateFormat = new SimpleDateFormat("yyyy-MM-dd-HH-mm-ss");
//生成时间戳
String dataString = simpleDateFormat.format(new Date());
//设置存放图片目录
String dir_name = System.getProperty("user.dir")+"\\异常图片";
System.out.println("异常图片目录:"+dir_name);
//防止出现图片目录被删除,所以先判断目录是否存在,如果不存在就新建一个目录
if (!(new File(dir_name).isDirectory())){
//如果不存在就创建一个目录
new File(dir_name).mkdir();
}
//调用方法获取页面截屏
File screenfile = driver.getScreenshotAs(OutputType.FILE);
//复制图片到指定目录,放到dir_name目录下,命名是时间戳+测试用例名称
FileUtils.copyFile(screenfile,new File(dir_name+"\\"+dataString+ScreenName+".jpg"));
//打印图片名称,
System.out.println("异常图片名称:"+dir_name+"\\"+dataString+ScreenName+".jpg");
}
}
在测试方法中调用:
@Test(description = "截屏")
public void testscreenshot() throws InterruptedException, IOException {
//正常请求的截图
Thread.sleep(1000);
Screenshot.Screenshot(driver,"test001");
Thread.sleep(1000);
//异常的截图放到catch中
try{
driver.findElementByName("百万播放");
}catch (Exception e){
Screenshot.Screenshot(driver,"test001");
}
}
生成目录时可能会遇到目录出现乱码的情况,进入idea的设置中找到File Encoding 都设置成UTF-8,然后再执行测试类就可以了
5.6 元素之间的滑动
控件的滑动可以用两种方式实现,第一种是TouchAction,第二种是swipe
封装的方法如下:
package com.yin.Appiumdemo;
import io.appium.java_client.AppiumDriver;
import io.appium.java_client.TouchAction;
import org.openqa.selenium.WebElement;
/*
封装方法,实现控件的滑动
*/
public class ElementHua {
//利用TouchAction 拖动控件
public static void Element_HuaWithTouchAction(AppiumDriver driver,WebElement element, String swipeway) throws InterruptedException {
//先获取控件的起点x坐标值
int xStartPoint = element.getLocation().getX();
//获取控件的宽度,计算出控件的x终点坐标值
int xEndPoint = xStartPoint+element.getSize().getWidth();
//获取控件的Y起点坐标值
int yStartPoint = element.getLocation().getY();
System.out.println("X起点坐标是"+xStartPoint);
System.out.println("X终点坐标是"+xEndPoint);
System.out.println("Y起点坐标是"+yStartPoint);
//利用TouchAction类实现拖动
TouchAction action = new TouchAction(driver);
if (swipeway.equals("左滑")){
Thread.sleep(1000);
//可以根据控件的宽度调整滑动多宽,可以调整20为30、40等等,以下方法类似
action.press(xEndPoint-20,yStartPoint+20).waitAction(1000).moveTo(xStartPoint,yStartPoint+20).release().perform();
Thread.sleep(1000);
}
if (swipeway.equals("右滑")){
Thread.sleep(1000);
action.press(xStartPoint+20,yStartPoint+20).waitAction(1000).moveTo(xEndPoint,yStartPoint+20).release().perform();
Thread.sleep(1000);
}
}
//利用滑动 滑动控件
public static void Element_HuaWithSwipe(AppiumDriver driver,WebElement element, String swipeway) throws InterruptedException {
//先获取控件的起点x坐标值
int xStartPoint = element.getLocation().getX();
//获取控件的宽度,计算出控件的x终点坐标值
int xEndPoint = xStartPoint+element.getSize().getWidth();
//获取控件的Y起点坐标值
int yStartPoint = element.getLocation().getY();
System.out.println("X起点坐标是"+xStartPoint);
System.out.println("X终点坐标是"+xEndPoint);
System.out.println("Y起点坐标是"+yStartPoint);
if (swipeway.equals("左滑")){
Thread.sleep(1000);
driver.swipe(xEndPoint-20,yStartPoint+20,xStartPoint+10,yStartPoint+20,500);
Thread.sleep(1000);
}
if (swipeway.equals("右滑")){
Thread.sleep(1000);
driver.swipe(xStartPoint+10,yStartPoint+20,xEndPoint-20,yStartPoint+20,500);
Thread.sleep(1000);
}
}
}
调用的方法是:
@Test(description = "控件滑动")
public void testactionswipe() throws InterruptedException {
//调用封装的方法实现控件滑动
driver.findElementByName("追番").click();
driver.findElementByName("时间表").click();
List<WebElement> elements = driver.findElementsById("tv.danmaku.bili:id/date_item");
WebElement element = elements.get(2);
ElementHua.Element_HuaWithTouchAction(driver,element,"左滑");
ElementHua.Element_HuaWithTouchAction(driver,element,"右滑");
ElementHua.Element_HuaWithSwipe(driver,element,"左滑");
ElementHua.Element_HuaWithSwipe(driver,element,"右滑");
}
5.7 控件点击操作
很简单,就用click()方法
控件的输入
用到sendkeys()
@Test(description = "控件的输入")
public void testsendkeys() throws InterruptedException {
Thread.sleep(1000);
driver.findElementById("tv.danmaku.bili:id/expand_search").click();
Thread.sleep(1000);
driver.findElementById("tv.danmaku.bili:id/search_src_text").sendKeys("test");
Thread.sleep(1000);
}
5.8 控件长按
@Test(description = "控件的长按")
public void testlongpress(){
//获取控件的X、Y坐标点,通过坐标点长按
WebElement element = driver.findElementByName("追番");
int X = element.getLocation().getX();
int Y = element.getLocation().getY();
TouchAction action = new TouchAction(driver);
//通过坐标点长按
action.longPress(X,Y).perform();
//通过定位到元素长按
action.longPress(driver.findElementByName("时间表")).perform();
}
5.9 控件的点击
TouchAction action = new TouchAction(driver);
//通过坐标点点击控件,
action.tap(X,Y).release().perform();
//通过输入控件的element点击控件
action.tap(element).release().perform();
tap比click点击准确,会点击控件的中间位置,click会存在点击不准确的情况。
5.10 Appium调起手机端chrome app浏览器
appium测试手机web的方法。
请参考https://www.jianshu.com/p/b46d3687fcfc
5.11 CSS定位
适用与webview的页面。
基础表达式部分:
高级表达式部分: