• 【App自动化测试】(十)特殊控件Toast识别



    前言
    本文为在霍格沃兹测试开发学社中学习到的一些技术写出来分享给大家,希望有志同道合的小伙伴可以一起交流技术,一起进步~ 😘

    1. toast介绍

    • Toast,简易的消息提示框。
    • 为了给当前视图显示一个浮动的显示块,与dialog不同它永远不会获得焦点。
    • Toast类的思想:尽可能不引人注意,同时还向用户显示信息希望他们看到。
    • Toast显示的时间有限,Toast会根据用户设置的显示时问后自动消失。
    • Toast本身是个系统级别的控件,它归属于系统settings,当一个app发送消息的时候,不是自己造出来的这个弹框,它是发给系统,由系统统一进行弹框,这类的控件不在app内,需要特殊的控件识别方法。

    2. toast定位

    在这里插入图片描述

    • appium使用uiautomator底层的机制来分析抓取toast,并且把toast放到控件树里面,但是它本身并不属于空间。

      private void addToastMsgToRoot(CharSequence tokenMSG){
        AccessibilityNodeInfo node = AccessibilityNodeInfo.obtain( );
        node.setText(tokenMSG);
        node.setclassName(Toast.class.getName( ) ) ;
        node.setPackageName( " com.android.settings"" ) ;
      
      this.children.add( new UiAutomationElement(node /* Accessibility)
      }
      
      • 1
      • 2
      • 3
      • 4
      • 5
      • 6
      • 7
      • 8
    • 必须使用xpath来查找:

      1. class定位: //*[@class='android.widget.Toast']
        注:一般不建议使用class来进行定位,但是Toast是个例外,一般只会有一个Toast的class,所以可以使用
      2. 文本内容定位: //*[contains(@text,"XXX")] (XXX是tosat信息中的部分内容)

    3. 实例演示

    测试程序:APIDemos

    测试内容:直接进入View下面的Pop menu 界面,然后点击操作按钮产生toast,并进行断言。

    步骤

    1. 先获取当前页面的Activity

    由于我们使用APIDemos来进行测试,所以可以跳过前面的步骤,直接打开我们要使用的Pop menu 界面去进行操作。但是正常app测试是无法这样操作的。

    运行命令:

    #iOS和Linu系统
    adb shell dumpsys window | grep mCurrent
    
    #Windows系统
    adb shell dumpsys window | find "mCurrent"
    
    • 1
    • 2
    • 3
    • 4
    • 5

    在这里插入图片描述

    1. 由于Toast无法使用定位工具直接去定位,如Appium Inspector,所以我们可以打印出PageSource,然后在去对Toast进行属性的查看
    from appium import webdriver
    from appium.webdriver.common.appiumby import AppiumBy
    
    class TestToast:
        def setup(self):
            #io.appium.android.apis/.ApiDemos
            # 创建一个字典
            desire_cap = {}
            # 平台
            desire_cap['platform'] = 'Android'
            # 手机系统版本
            desire_cap['platformVersion'] = '6.0'
            # 设备名
            desire_cap['deviceName'] = '127.0.0.1:7555'
            # app 包名
            desire_cap['appPackage'] = 'io.appium.android.apis'
            # app 页面名
            desire_cap['appActivity'] = '.view.PopupMenu1'
            desire_cap['noReset'] = 'true'
            self.driver = webdriver.Remote("http://127.0.0.1:4723/wd/hub", desire_cap)
            self.driver.implicitly_wait(10)
    
        def teardown(self):
            self.driver.quit()
    
        def test_toast(self):
            #点击 Make a Popup!
            self.driver.find_element(AppiumBy.ACCESSIBILITY_ID,"Make a Popup!").click()
            #点击 点击search
            self.driver.find_element(AppiumBy.CSS_SELECTOR, "*[text='Search']").click()
            #打印pagesource
            print(self.driver.page_source)
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32

    PageSource内容:通过PageSource可以知道,此处Toast的class属性为"android.widget.Toast",它的text属性为"Clicked popup menu item Search"。

    
    <hierarchy index="0" class="hierarchy" rotation="3" width="900" height="1600">
      <android.widget.FrameLayout index="0" package="io.appium.android.apis" class="android.widget.FrameLayout" text="" checkable="false" checked="false" clickable="false" enabled="true" focusable="false" focused="false" long-clickable="false" password="false" scrollable="false" selected="false" bounds="[0,0][900,1600]" displayed="true">
        <android.view.ViewGroup index="0" package="io.appium.android.apis" class="android.view.ViewGroup" text="" resource-id="android:id/decor_content_parent" checkable="false" checked="false" clickable="false" enabled="true" focusable="false" focused="false" long-clickable="false" password="false" scrollable="false" selected="false" bounds="[0,0][900,1600]" displayed="true">
          <android.widget.FrameLayout index="0" package="io.appium.android.apis" class="android.widget.FrameLayout" text="" resource-id="android:id/action_bar_container" checkable="false" checked="false" clickable="false" enabled="true" focusable="false" focused="false" long-clickable="false" password="false" scrollable="false" selected="false" bounds="[0,45][900,150]" displayed="true">
            <android.view.ViewGroup index="0" package="io.appium.android.apis" class="android.view.ViewGroup" text="" resource-id="android:id/action_bar" checkable="false" checked="false" clickable="false" enabled="true" focusable="false" focused="false" long-clickable="false" password="false" scrollable="false" selected="false" bounds="[0,45][900,150]" displayed="true">
              <android.widget.TextView index="0" package="io.appium.android.apis" class="android.widget.TextView" text="Views/Popup Menu" checkable="false" checked="false" clickable="false" enabled="true" focusable="false" focused="false" long-clickable="false" password="false" scrollable="false" selected="false" bounds="[30,71][361,123]" displayed="true" />
            android.view.ViewGroup>
          android.widget.FrameLayout>
          <android.widget.FrameLayout index="1" package="io.appium.android.apis" class="android.widget.FrameLayout" text="" resource-id="android:id/content" checkable="false" checked="false" clickable="false" enabled="true" focusable="false" focused="false" long-clickable="false" password="false" scrollable="false" selected="false" bounds="[0,150][900,1600]" displayed="true">
            <android.widget.LinearLayout index="0" package="io.appium.android.apis" class="android.widget.LinearLayout" text="" checkable="false" checked="false" clickable="false" enabled="true" focusable="false" focused="false" long-clickable="false" password="false" scrollable="false" selected="false" bounds="[0,150][900,1600]" displayed="true">
              <android.widget.Button index="0" package="io.appium.android.apis" class="android.widget.Button" text="Make a Popup!" content-desc="Make a Popup!" checkable="false" checked="false" clickable="true" enabled="true" focusable="true" focused="false" long-clickable="false" password="false" scrollable="false" selected="false" bounds="[331,150][568,240]" displayed="true" />
            android.widget.LinearLayout>
          android.widget.FrameLayout>
        android.view.ViewGroup>
      android.widget.FrameLayout>
      <android.widget.Toast index="1" package="com.android.settings" class="android.widget.Toast" text="Clicked popup menu item Search" displayed="true" />
    hierarchy>
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    1. 定位方式一:通过class属性进行定位

    一般一个页面只用一个toast类,因此可以使用class属性定位

    from appium import webdriver
    from appium.webdriver.common.appiumby import AppiumBy
    
    class TestToast:
        def setup(self):
            #io.appium.android.apis/.ApiDemos
            # 创建一个字典
            desire_cap = {}
            # 平台
            desire_cap['platform'] = 'Android'
            # 手机系统版本
            desire_cap['platformVersion'] = '6.0'
            # 设备名
            desire_cap['deviceName'] = '127.0.0.1:7555'
            # app 包名
            desire_cap['appPackage'] = 'io.appium.android.apis'
            # app 页面名
            desire_cap['appActivity'] = '.view.PopupMenu1'
            self.driver = webdriver.Remote("http://127.0.0.1:4723/wd/hub", desire_cap)
            self.driver.implicitly_wait(10)
    
        def teardown(self):
            self.driver.quit()
    
        def test_toast(self):
            #点击 Make a Popup!
            self.driver.find_element(AppiumBy.ACCESSIBILITY_ID,"Make a Popup!").click()
            #点击 点击search
            self.driver.find_element(AppiumBy.CSS_SELECTOR, "*[text='Search']").click()
    	#通过class属性定位Toast
            toast=self.driver.find_element(AppiumBy.XPATH,'//*[@class="android.widget.Toast"]')
            assert "Clicked popup menu item Search"==toast.text
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    1. 定位方式二:通过toast展现的文字进行定位

    使用contains方式,通过toas的text属性去模糊匹配其中的内容,从而完成Toast的定位。

    from appium import webdriver
    from appium.webdriver.common.appiumby import AppiumBy
    
    class TestToast:
        def setup(self):
            #io.appium.android.apis/.ApiDemos
            # 创建一个字典
            desire_cap = {}
            # 平台
            desire_cap['platform'] = 'Android'
            # 手机系统版本
            desire_cap['platformVersion'] = '6.0'
            # 设备名
            desire_cap['deviceName'] = '127.0.0.1:7555'
            # app 包名
            desire_cap['appPackage'] = 'io.appium.android.apis'
            # app 页面名
            desire_cap['appActivity'] = '.view.PopupMenu1'
            self.driver = webdriver.Remote("http://127.0.0.1:4723/wd/hub", desire_cap)
            self.driver.implicitly_wait(10)
    
        def teardown(self):
            self.driver.quit()
    
        def test_toast(self):
            #点击 Make a Popup!
            self.driver.find_element(AppiumBy.ACCESSIBILITY_ID,"Make a Popup!").click()
            #点击 点击search
            self.driver.find_element(AppiumBy.CSS_SELECTOR, "*[text='Search']").click()
    	#模糊匹配Toast中的Text属性的文本内容,完成Toast的定位
            toast=self.driver.find_element(AppiumBy.XPATH,'//*[contains(@text,"Clicked popup")]')
            assert "Clicked popup menu item Search"==toast.text
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32

    文末说明:
    接口测试中我们很容易混淆Session、cookie和token,你知道他们有什么区别吗?

    快来跟我一起看,一篇文章让你了解三者的区别。😎
    ⬇⬇⬇⬇⬇⬇⬇
    👍👍👍:接口测试经典面试题:Session、cookie、token有什么区别?

  • 相关阅读:
    maven了解
    数据挖掘技术-绘制箱线图
    【锂离子电池】
    TypeScripe笔记:any、unknown、never、void、null 和 undefined 及其比较
    React Redux 如何更新购物车中的产品数量
    北京易华录大数据高级研发师1103面试题
    Linux系统内核作用
    Elasticsearch使用系列-.NET6对接Elasticsearch
    21天学习挑战赛-《Autosar从入门到精通-实战篇》
    Java之异常处理
  • 原文地址:https://blog.csdn.net/gjj920318/article/details/127996785