美文网首页
Appium学习(二)--工具介绍、服务关键字、元素定位

Appium学习(二)--工具介绍、服务关键字、元素定位

作者: 零下的雨 | 来源:发表于2019-01-14 18:21 被阅读0次

    一、使用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”


    image.png

    方法二:查看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

    image.png

    注:也可以问Android开发包名和activity,执行appium自动化测试安装app时在appium图形化视图中看日志也可以查看到包名和activity。

    二、使用Android工具查找元素

    进入sdk\tools\uiautomatorview.bat 打开这个工具


    image.png

    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();
    
    image.png

    要定位到红框里的元素,首先找到它的父级目录,父级目录的classname是android.support.v7.widget.RecyclerView
    红框里的元素的classname是android.view.ViewGroup,他是第1个元素,所以
    写android.view.ViewGroup[1],如果是最后一个就可以写[last()],如果要定位倒数第2个就写[last()-1]


    image.png

    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:: 定位子孙元素:


    image.png

    通过父级去找:

            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();
    

    以上代码要定位到第二个视频,打开后再返回


    image.png

    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 滑动操作

    image.png
    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)


    image.png

    封装的代码:

    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("坐标定位成功");
        }
    
    image.png

    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

    image.png
    手机坐标、首页导航滑动前面已经讲过,省略。

    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,然后再执行测试类就可以了


    image.png

    5.6 元素之间的滑动

    image.png
    image.png image.png image.png

    控件的滑动可以用两种方式实现,第一种是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()方法

    image.png

    控件的输入
    用到sendkeys()

    image.png
        @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 控件长按

    image.png
        @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的页面。
    基础表达式部分:

    image.png image.png

    高级表达式部分:


    image.png

    相关文章

      网友评论

          本文标题:Appium学习(二)--工具介绍、服务关键字、元素定位

          本文链接:https://www.haomeiwen.com/subject/qintdqtx.html