【Appium】基础5(toast和参数化)

【Appium】 课程全笔记(录播+直播课)链接:

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

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

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

Appium基础4(显式等待)

Appium基础5(toast和参数化)

Appium基础6(webview)

Appium_企业微信实战课1(非PO,增加和删除联系人)

Appium_企业微信实战课2(PO–增加联系人)


Toast

含义

  • 为了给当前视图显示一个浮动的显示块,与dialog不同它永远不会获得焦点
  • 显示时间有限,根据用户设置的显示时间后自动消失
  • 本身是个系统级别的控件,它归属系统settings,当一个app发送消息的时候,不是自己造出来的这个弹框,它是发给系统,由系统统一进行弹框,这类的控件不在app内、需要特殊的控件识别方法

toast定位

  • appium使用uiautomator底层的机制来分析抓取toast,并且把toast放到控件树里面,但本身并不属于控件
  • automationName:uiautomator2 这个是appium本身的设置就自带的,不需要额外添加,默认就是uiautomator2
  • getPageSource是无法找到toast的
  • 必须使用xpath去查找
    • //*[@class=“android.widget.Toast”]
    • //*[contains(@text,“xxxxx”)]

实例:appium自带的app测试toast

  • adb shell dumpsys window | findstr mCurrent 这个命令可以找到当前的activity,不知道Android高版本是不是还ok,由于api demo权限高,可直接跳到这个activity运行,其他app就不ok了
  • driver.page_source可以打印当前的页面,可以找到toast的伪控件
  • 打印toast的text出来
driver.page_source打印出来的东西,包含toast
<?xml version='1.0' encoding='UTF-8' standalone='yes' ?>
<hierarchy index="0" class="hierarchy" rotation="3" width="810" height="1440">
  <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][810,1440]" 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][810,1440]" 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,41][810,136]" 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,41][810,136]" 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="[27,65][324,111]" 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,136][810,1440]" 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,136][810,1440]" 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="[297,136][513,217]" displayed="true" />
        </android.widget.LinearLayout>
      </android.widget.FrameLayout>
    </android.view.ViewGroup>
  </android.widget.FrameLayout>
  
  #这里就找到了Tast的控件了
  <android.widget.Toast index="1" package="com.android.settings" class="android.widget.Toast" text="Clicked popup menu item Search" checkable="false" checked="false" clickable="false" enabled="false" focusable="false" focused="false" long-clickable="false" password="false" scrollable="false" selected="false" bounds="[0,0][0,0]" displayed="false" />
</hierarchy>
代码
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":"io.appium.android.apis",
            "appActivity":"io.appium.android.apis.view.PopupMenu1",
            "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.打开appium的演示app
        2.直接进入到测试toast的界面
        3.点击显示toast的按钮,然后通过driver.page_source获取页面
        4.找到toast的伪控件
        5.打印出toast的值出来
        :return:
        """
        #点击Make a Popup的控件
        self.driver.find_element(By.XPATH,'//*[@text="Make a Popup!"]').click()
        #点击search的控件
        self.driver.find_element(By.XPATH, '//*[@text="Search"]').click()
        #打印整个布局页面的xml出来
        print(self.driver.page_source)
        #打印出toast的值
        print(self.driver.find_element(By.XPATH, '//*[contains(@text,"popup menu")]').text)

参数化

一些小细节

  • 参数化要解决的是一个用例可以复用的问题,比如一个用例重复使用不同的数据,就可以使用参数化,比如同一个用例,有搜索股价,比较股价,都是同一个方法,只是数据不太一样
  • @pytest.mark.parametrize(‘searchkey,type,price’,[
    (‘alibaba’,‘BABA’,180),
    (‘xiaomi’,‘01810’,10)
  • 用上面的方法去使用参数化
  • def test_search(self,searchkey,type,price) 函数的参数要和参数化的参数的数量一样,字符串也要一样
  • 一个用例,有2组参数化,就会运行两次setup和teardown的方法
  • 使用self.driver.find_element(By.ID,“com.xueqiu.android:id/search_input_text”).send_keys(f"{searchkey}“),使用f”{searchkey}"是一个好东西,可以搭配参数化使用
代码
from appium import webdriver
from appium.webdriver.common.mobileby import MobileBy as By
from selenium.webdriver.support import expected_conditions
from selenium.webdriver.support.wait import WebDriverWait
import pytest

class TestFind():
    #设置caps的值
    def setup(self):
        self.desire_cap= {
            #默认是Android
            "platformName":"android",
            #adb devices的sn名称
            "deviceName":"127.0.0.1:7555",
            #包名
            "appPackage":"com.xueqiu.android",
            #activity名字
            "appActivity":".view.WelcomeActivityAlias",
            "noReset":"true",
            "unicodeKeyboard":True
        }
        #运行appium,前提是要打开appium server
        self.driver=webdriver.Remote("http://127.0.0.1:4723/wd/hub",self.desire_cap)
        self.driver.implicitly_wait(5)

    #这个加不加都行,因为参数化运行都会有setup的,setup就是启动app的过程了,这个就显得有点多余了
    #但好像setup并不会初始化整个app,还会停留在前一个页面上,所以还是加上比较好
    def teardown(self):
        self.driver.find_element(By.XPATH,'//*[@text="取消"]').click()

    #这是参数化的函数,第一部分是参数化的名字,得和下面的函数参数一模一样,用字符串包含进去
    #列表里面的元祖接受具体的参数化的数据,用逗号隔开,和list一样
    @pytest.mark.parametrize('searchkey,type,price',[
        ('alibaba','BABA',180),
        ('xiaomi','01810',10)
    ])
    #参数哈的函数的参数要和上面的参数名字保持一致
    def test_search(self,searchkey,type,price):
        """
        1.打开雪球app
        2.点击搜索输入框
        3.向搜索输入框输入“阿里巴巴”
        4.在搜索的结果里选择阿里巴巴,然后点击
        5.获取这只上香港 阿里巴巴的股价,并判断这只股价的价格>200
        6.通过参数化的方法,用一个用例判断阿里巴巴和小米的股价
        :return:
        """
        #显示等待进入主页,等主页的元素都加载好了
        WebDriverWait(self.driver, 15).until(expected_conditions.element_to_be_clickable((By.XPATH,'//*[@text="我的"]')))
        #点击搜索框
        self.driver.find_element(By.ID,"com.xueqiu.android:id/tv_search").click()
        #向搜索框输入阿里巴巴,小米等参数化的东西f"{searchkey}"是一个好用的东西
        self.driver.find_element(By.ID,"com.xueqiu.android:id/search_input_text").send_keys(f"{searchkey}")
        #找到搜索框预览结果的阿里巴巴,并点击
        self.driver.find_element(By.XPATH,f"//*[@text='{type}']").click()
        #选择HK股价的元素,这里是通过父类的方法去定位的
        current_price=self.driver.find_element(By.XPATH,f"//*[@text='{type}']/../../..//*[@resource-id='com.xueqiu.android:id/current_price']")
        #提取股价的text属性
        current_price=float(current_price.text)

        #判断股价是否大于200
        assert current_price > price
3 Likes