• 史上最全 Appium 自动化测试从入门到框架实战精华学习笔记(二)


    本文为霍格沃兹测试学院学员学习笔记,进阶学习文末加群。

    本系列文章汇总了从 Appium 自动化测试从基础到框架高级实战中,所涉及到的方方面面的知识点精华内容(如下所示),希望对大家快速总结和复习有所帮助。

    Appium 自动化测试从基础到框架实战

    1. Appium 基础 1 (环境搭建和简介) ****

    2. Appium 基础 2 (元素定位和元素常用方法) ****

    3. Appium 基础 3 (手势操作和 uiautomator 查找元素)

    4. Appium 基础 4 (显式等待)

    5. Appium 基础 5 (toast 和参数化)

    6. Appium 基础 6 (webview)

    7. Appium_ 企业微信练习 (非 PO,增加和删除联系人)

    8. Appium_ 企业微信练习 ( PO--增加联系人)

    本文为第二篇,主要讲解 Appium 手势操作、查找元素、显示等待(附实例代码)。

    Appium 的触屏操作

    滑动小案例

    1. 进入雪球应用

    2. 再主页从下往上滑动

    3. 避免使用坐标(代码用获取屏幕的长宽来解决这个问题)

    代码

    from time import sleep  
    from appium import webdriver  
    from appium.webdriver.common.touch_action import TouchAction  
      
    class TestFind():  
        def setup(self):  
            self.desire_cap= {  
                "platformName":"android",  
                "deviceName":"127.0.0.1:7555",  
                "appPackage":"com.xueqiu.android",  
                "appActivity":".view.WelcomeActivityAlias",  
                "noReset":"true",  
                "unicodeKeyboard":True  
            }  
            self.driver=webdriver.Remote("http://127.0.0.1:4723/wd/hub",self.desire_cap)  
            self.driver.implicitly_wait(5)  
      
        def test_search(self):  
            """  
            1.进入雪球应用  
            2.再主页从下往上滑动  
            3.避免使用坐标  
            :return:  
            """  
            #由于雪球真的是太慢了,所以睡10秒  
            sleep(10)  
            #定义一个TouchAcion对象  
            aciton=TouchAction(self.driver)  
            #获取整个屏幕的右下角的坐标  
            window_rect=self.driver.get_window_rect()  
            #提取屏幕的最大的宽  
            width=window_rect["width"]  
            #提取屏幕的最大的高度  
            height=window_rect['height']  
            #x的坐标定义为最大宽的一半,也就是中心的x坐标  
            x1=int(width/2)  
            #定义起始的y坐标,在4/5的底部位置  
            y_start=int(height* 4/5)  
            #定义终点的y坐标,在1/5顶部的位置,这样就可以模拟从下往上滑动的动作  
            y_end=int(height* 1/5)  
            #先press点击初始的坐标,然后按住不放等2秒再move_to到终点坐标,然后再release()释放坐标点,用perform()去执行一系列action操作  
            aciton.press(x=x1,y=y_start).wait(2000).move_to(x=x1,y=y_end).release().perform()  
            #重复两次,看的效果更明显  
            aciton.press(x=x1, y=y_start).wait(2000).move_to(x=x1, y=y_end).release().perform()  
            aciton.press(x=x1, y=y_start).wait(2000).move_to(x=x1, y=y_end).release().perform()  
            sleep(3)  
    

    滑动多点解锁

    1. 得下载一个叫手势密码锁的 App,百度一下有

    2. 进入解锁的页面

    3. 设置解锁密码为一个7字

    4. 意外发现 Appium 可以指定去不同的初始的 activity,好像也是看应用的

    代码

    from time import sleep  
    from appium import webdriver  
    from appium.webdriver.common.mobileby import MobileBy as By  
    from appium.webdriver.common.touch_action import TouchAction  
      
    class TestFind():  
        def setup(self):  
            self.desire_cap = {  
                "platformName": "android",  
                "deviceName": "127.0.0.1:7555",  
                "appPackage": "cn.kmob.screenfingermovelock",  
                "appActivity": "com.samsung.ui.MainActivity",  
                "noReset": "true"  
            }  
            self.driver = webdriver.Remote("http://127.0.0.1:4723/wd/hub", self.desire_cap)  
            self.driver.implicitly_wait(5)  
      
        def test_search(self):  
            """  
            1.进入解锁的页面  
            2.设置解锁密码为一个7字  
            3.意外发现appium可以指定去不同的初始的activity,好像也是看应用的  
            :return:  
            """  
            sleep(3)  
            #定义一个TouchAcion对象  
            aciton=TouchAction(self.driver)  
            #找到7个坐标点,通过连续的press,wait,move_to,最后释放手势release(),然后perform()执行即可  
            aciton.press(x=142,y=190).wait(200).move_to(x=408,y=190).wait(200).move_to(x=678,y=190).wait(200).move_to(x=678,y=464) \  
            .wait(200).move_to(x=678,y=740).release().perform()  
            sleep(2)  
    

    UIAutomator 查找元素

    优缺点

    • 优点

      • xpath 定位速度慢

      • UIAutomator 是 Android 的工作引擎,速度快

      • 滚动查找很方便

    • 缺点

      • 表达式书写复杂,容易写错 IDE 没有提示

    定位方式

    • 通过 resource-id 定位

    • 通过 classname 定位

    • 通过 content-desc 定位

    • 通过文本定位

    • 组合定位

    • 通过父子关系定位

    用法

    • driver.find_element_by_android_uiautomator("表达式")

    • 注:外层要用单引号,内层的字符串用双引号,因为本来就是 Java,Java 双引号才表示字符串

    • 通过文本定位

      • new UiSelector().text("text文本")
    • 通过 textContains 模糊匹配

      • new UiSelector().textContains("text文本")
    • 通过某个文本开头匹配

      • new UiSelector().textStartWith("text文本")
    • 正则表达式匹配

      • new UiSelector().textMatches("text文本")
    • 组合定位

      • 比如 id 与 text 的属性组合:driver.find_element_by_android_uiautomator('new UiSelector().resourceId("com.xueqiu.android:id/login_account").text("我的")')
    • 父子关系定位:childSelector,先定位到父类,再用 childSelector 来定位子类

      • driver.find_element_by_android_uiautomator('new UiSelector().resourceId("com.xueqiu.android:id/login_account").childSelector(text("股票"))')
    • 兄弟定位:fromParent

      • driver.find_element_by_android_uiautomator('new UiSelector().resourceId("com.xueqiu.android:id/login_account").fromParent(text("股票"))')
    代码
    from time import sleep  
    from appium import webdriver  
    from appium.webdriver.common.touch_action import TouchAction  
      
    class TestFind():  
        def setup(self):  
            self.desire_cap= {  
                "platformName":"android",  
                "deviceName":"127.0.0.1:7555",  
                "appPackage":"com.xueqiu.android",  
                "appActivity":".view.WelcomeActivityAlias",  
                "noReset":"true",  
                "unicodeKeyboard":True  
            }  
            self.driver=webdriver.Remote("http://127.0.0.1:4723/wd/hub",self.desire_cap)  
            self.driver.implicitly_wait(5)  
      
        def test_search(self):  
            """  
            1.打开雪球app  
            2.点击我的,进入到个人信息页面  
            3.点击登录,进入到登录页面  
            4.输入用户名,输入密码  
            5.点击登录  
            6.弹出手机号输入失败的提示,并assert这个提示对不对  
            :return:  
            """  
            #雪球太慢了,只能10秒了,懒得用显示等等  
            sleep(10)  
            #在首页找到我的元素,然后点击  
            self.driver.find_element_by_android_uiautomator('new UiSelector().text("我的")').click()  
            #不睡2秒回导致下一个页面的元素刷新太快识别不到  
            sleep(2)  
            #识别账号密码登录的元素,然后点击  
            self.driver.find_element_by_android_uiautomator('new UiSelector().text("帐号密码登录")').click()  
            #不睡2秒回导致下一个页面的元素刷新太快识别不到  
            sleep(2)  
            #输入账号名为tongtong  
            self.driver.find_element_by_android_uiautomator('new UiSelector().resourceId("com.xueqiu.android:id/login_account")').send_keys("tongtong")  
            #输入密码为tongtong  
            self.driver.find_element_by_android_uiautomator('new UiSelector().resourceId("com.xueqiu.android:id/login_password")').send_keys("tongtong")  
            #不睡2秒回导致下一个页面的元素刷新太快识别不到  
            sleep(2)  
            #点击登录按钮  
            self.driver.find_element_by_android_uiautomator('new UiSelector().text("登录")').click()  
            #不睡2秒回导致下一个页面的元素刷新太快识别不到  
            sleep(2)  
            #找到错误提示框,里面有一个确定的元素  
            login_incorrect=self.driver.find_element_by_android_uiautomator('new UiSelector().text("确定")')  
            #当确定的元素可见,表示登录失败,用例pass  
            assert login_incorrect.is_displayed()  
    

    滑动元素查找

    • 有一些页面有持续滑动的能力,比如微博,没有分页,可以一直滑动,UIAutomator 提供了滑动的很好的方法。

    • driver.find_element_by_android_uiautomator(‘new UiScrollable(new UiSelector().’
      ‘scrollable(true).instance(0)).’
      ‘scrollIntoView(new UiSelector().textContains(“病人”).’
      ‘instance(0));’).click()

    • 注意:虚拟机和真机不一样,有时候真机的滑动是ok的,有时候虚拟机的不ok

    代码

    from time import sleep  
    from appium import webdriver  
    from appium.webdriver.common.touch_action import TouchAction  
      
    class TestFind():  
        def setup(self):  
            self.desire_cap= {  
                "platformName":"android",  
                "deviceName":"127.0.0.1:7555",  
                "appPackage":"com.xueqiu.android",  
                "appActivity":".view.WelcomeActivityAlias",  
                "noReset":"true",  
                "unicodeKeyboard":True  
            }  
            self.driver=webdriver.Remote("http://127.0.0.1:4723/wd/hub",self.desire_cap)  
            self.driver.implicitly_wait(5)  
      
        def test_search(self):  
            """  
            0.你的雪球app先关注一个人,然后往下滑,找到一个关键字,用textContains来模糊匹配  
            1.打开雪球app  
            2.点击关注,让屏幕往下滑,直到找到病人的模糊匹配的text元素后点击  
            :return:  
            """  
            #雪球太慢了,只能10秒了,懒得用显示等等  
            sleep(10)  
            #点击关注的元素  
            self.driver.find_element_by_android_uiautomator('new UiSelector().text("关注")').click()  
            #睡4秒,怕跳转页面太快,搜索不到元素  
            sleep(4)  
            self.driver.find_element_by_android_uiautomator('new UiScrollable(new UiSelector().'  
                                                            'scrollable(true).instance(0)).'  
                                                            'scrollIntoView(new UiSelector().textContains("病人").'  
                                                            'instance(0));').click()  
            sleep(4)  
    

    强制等待、隐式等待、显示等待

    三者的特点

    • 强制等待是 sleep,强烈不推荐,设定的时间太固定,如果是模拟器等待3秒,真机可能只需要等待2秒

    • driver.implicitly.wat(timeout),贯穿全部元素的等待,只需要设定一次即可,通常是在创建 driver 的时候后的代码运行,是 dom 建立之后的等待;

    • 显示等待是在客户端的等待:引用两个包和一个例子

      • from selenium.webdriver.support.wait import WebDriverWait

      • from selenium.webdriver.support import expected_conditions

      • WebDriverWait(self.driver,10).until(expected_conditions.element_to_be_clickable(locator))

    显示等待的简介

    • 显示等待与隐式等待相对,显示等待必须在每一个需要等待的元素前面进行声明

    • 是针对某个特定的元素设置等待时间,在设置时间内,默认美格一段时间检测一次当前某个元素是否存在

    • 如果在规定的时间内找到元素,则直接执行,即找到元素就执行相关操作

    • 如果超过设置时间检测不到就抛出异常,默认检测频率为0.5s,默认抛出的异常时NoSuchElementException

    • 用到的两个常用类

      • WebDriverWait

      • expected_condition

    为什么要用显示等待,为什么隐式等待无法替代显示等待?

    • 显示等待可以等待动态加载的 AJax 元素,需要配合 expected_condition 来检查条件

    • 一般页面上元素的呈现顺序是

      • 首先出现 title;

      • 然后是 dom 树的出现,presence 还不完整,dom 树出现就是隐式等待了,但此时的元素可能还没有是可点击的状态,所以只用隐式等待,使用 click 方法,肯定会报错的;

      • CSS 出现:可见 visbility;

      • JS 的出现,JS 特效执行:可点击 clickable;

    • HTML 文档是自上而下加载的

    • JS 文件加载会阻塞 HTML 内容的加载,有些 JS 异步加载的方式来完成 JS 的加载

    • 样式表下载完成之后跟之前的样式表一起进行解析,会对之前那的与元素重新渲染

    • presence-visibility-clickabe,元素出现-可见-可点击,是元素的三个性质,当 DOM 树出现时,定位元素可能已经显示出来了,但是可见和可点击的属性可能还没加载出来,这时候元素的一些方法是不可用的,比如 element.click(),要等到 JS 渲染出来以后,元素的 click 属性才可以用

      • 对应element.is_displayed()

      • 对应element.is_selected()

      • 对应element.is_enabled()

    JS 的同步加载和异步加载

    • 同步加载:同步模式,又称阻塞模式,会阻止浏览器的后续处理,停止了后续的解析,因此停止了后续的文件加载(如图像)、渲染、代码执行。

    • 异步加载:异步加载又叫非阻塞,浏览器在下载执行 JS 同时,还会继续进行后续页面的处理。

    WebDriverWait 用法

    • WebDriverWait(self, driver, timeout, poll_frequency=POLL_FREQUENCY, ignored_exceptions=None)

    • driver:浏览器驱动

    • timeout:超时时间,单位秒

    • poll_frequency:检查的间隔步长,默认是0.5s

    • ignored_exceptions:超时最后的抛出的异常,默认是NoSuchElementException

    • 通常我们只会用到 driver 和 timeout

    • WebDriverWait().unti(self, method, message='') or until_not()的方法:

      • method:在等待期间,每个一段时间( init 中的poll_frequency)调用这个传入的方法,直到返回值不是 False

      • message:如果超时,抛出 TimeoutException,将 message 传入异常

      • until not 是当某个元素小时或什么条件则继续执行,参数也相同

    expected_conditions 类

    • Appium 直接帮我们封装好了类,只需要传参数即可,比如我们使用的是 click(),只需要判断这个元素是否可点击属性才继续点击

    • 用法:expected_conditions.element_to_be_clickable(locator),其中 locator 就是:(By.ID, "com.xueqiu.android:id/tv_search")

    • 常用的几个如下:

    • expected_conditions.element_to_be_clickable:元素是否可点击

    • expected_conditions.presence_of_element_located:元素是否被加到 Dom 树里面

    • expected_conditions.visibility_of_element_located:元素是否可见

    Lambda 获取元素

    #可以获取到元素  
    element = WebDriverWait(self.driver, 10).until(lambda x: x.find_element(By.XPATH,'//*[@text="我的"]'))  
    #这里找到元素后,不用等待,实测证明过了  
    element.click()  
    

    实例代码

    from appium import webdriver  
    from selenium.webdriver.support import expected_conditions  
    from selenium.webdriver.support.wait import WebDriverWait  
    from appium.webdriver.common.mobileby import MobileBy as By  
      
    class TestFind():  
        def setup(self):  
            self.desire_cap= {  
                "platformName":"android",  
                "deviceName":"127.0.0.1:7555",  
                "appPackage":"com.xueqiu.android",  
                "appActivity":".view.WelcomeActivityAlias",  
                "noReset":"true",  
                "unicodeKeyboard":True  
            }  
            self.driver=webdriver.Remote("http://127.0.0.1:4723/wd/hub",self.desire_cap)  
            self.driver.implicitly_wait(5)  
      
        def test_search(self):  
            """  
            1.打开雪球app  
            2.点击我的,进入到个人信息页面  
            3.点击登录,进入到登录页面  
            4.输入用户名,输入密码  
            5.点击登录  
            6.弹出手机号输入失败的提示,并assert这个提示对不对  
            :return:  
            """  
            #By.name方法不是对应text的,千万不要用  
            #self.driver.find_element(By.NAME,"我的")  
            #用显示等待,element_to_be_clickable(locator),里面的locator记得用元祖  
            WebDriverWait(self.driver, 15).until(expected_conditions.element_to_be_clickable((By.XPATH,'//*[@text="我的"]')))  
            #在首页找到我的元素,然后点击  
            self.driver.find_element(By.XPATH,'//*[@text="我的"]').click()  
            '''  
            lambda返回元素  
            element = WebDriverWait(self.driver, 10).until(lambda x: x.find_element(By.XPATH,'//*[@text="我的"]'))  
            这里找到元素后,不用等待,实测证明过了  
            element.click()  
            '''  
            #显示等待找到账号密码登录的元素  
            WebDriverWait(self.driver, 15).until(expected_conditions.element_to_be_clickable((By.XPATH,'//*[@text="帐号密码登录"]')))  
            #识别账号密码登录的元素,然后点击  
            self.driver.find_element_by_android_uiautomator('new UiSelector().text("帐号密码登录")').click()  
            #显示等待找到账号的元素  
            WebDriverWait(self.driver, 15).until(  
                expected_conditions.element_to_be_clickable((By.XPATH, '//*[@resource-id="com.xueqiu.android:id/login_account"]')))  
            #输入账号名为tongtong  
            self.driver.find_element_by_android_uiautomator('new UiSelector().resourceId("com.xueqiu.android:id/login_account")').send_keys("tongtong")  
            #输入密码为tongtong  
            self.driver.find_element_by_android_uiautomator('new UiSelector().resourceId("com.xueqiu.android:id/login_password")').send_keys("tongtong")  
            #显示等待找到登录的按钮  
            WebDriverWait(self.driver, 15).until(  
                expected_conditions.element_to_be_clickable((By.XPATH, '//*[@text="登录"]')))  
            #点击登录按钮  
            self.driver.find_element_by_android_uiautomator('new UiSelector().text("登录")').click()  
            #显示等待找到确定的元素  
            WebDriverWait(self.driver, 15).until(  
                expected_conditions.element_to_be_clickable((By.XPATH, '//*[@text="确定"]')))  
            #找到错误提示框,里面有一个确定的元素  
            login_incorrect=self.driver.find_element_by_android_uiautomator('new UiSelector().text("确定")')  
            #当确定的元素可见,表示登录失败,用例pass  
            assert login_incorrect.is_displayed()
    

    更多内容,我们在后续文章分享。


    **
    来霍格沃兹测试开发学社,学习更多软件测试与测试开发的进阶技术,知识点涵盖web自动化测试 app自动化测试、接口自动化测试、测试框架、性能测试、安全测试、持续集成/持续交付/DevOps,测试左移、测试右移、精准测试、测试平台开发、测试管理等内容,课程技术涵盖bash、pytest、junit、selenium、appium、postman、requests、httprunner、jmeter、jenkins、docker、k8s、elk、sonarqube、jacoco、jvm-sandbox等相关技术,全面提升测试开发工程师的技术实力

    点击获取更多信息

  • 相关阅读:
    RubyConf的podcast
    一篇很好的英语学习文章:一个孤独而封闭世界――英语口语
    新浪和搜狐的读书频道
    新想法:个性化的RSS
    代码搜索:Koders
    我看到的Web 2.0: 自组织的大众化参与
    土豆网的后舍男孩挺搞笑的
    可以给pdf加批注的软件VeryPDF PDF Editor
    张海迪写的描写英语学习经验的书《美丽的英语》
    Fowler出来推荐Rake了(基于Ruby的build工具)
  • 原文地址:https://www.cnblogs.com/hogwarts/p/15984637.html
Copyright © 2020-2023  润新知