Appium编写脚本中的那些事
官方文档地址:http://appium.io/docs/en/about-appium/intro/
一、常用的元素定位方式
元素定位的官方文档
find_element_by_id (resource-id)
find_element_by_accessibility_id (content-desc)
find_element_by_xpath
find_element_by_android_uiautomator
关键的Attribute:resource-id,content-desc,text,bounds,clickable
常用 XPath 相对定位表达式:
二、常用自动化动作支持
click
sendkeys
swipe
touch action
三、capability设置
automationName 默认使⽤用 uiautomator
autoGrantPermissions ⾃自动赋予 App 权限
noReset fullReset 是否在测试前后重置相关环境
unicodeKeyBoard resetKeyBoard 是否需要输入非英文之外的语言并在测试完成后重置输入法
四、appium设备交互api
发短信,打电话
APP处理:get_performance_data 获取CPU,内存,电量,网络数据
五、等待
1、隐式等待:服务端(Appium)会在特定的超时时间内重试多次寻找控件
self.driver.implicitly_wait(20)
2、显式等待:在客户端(用例端)根据更灵活的条件循环等待条件满足
from selenium.webdriver.common.by import By from selenium.webdriver.support import expected_conditions from selenium.webdriver.support.wait import WebDriverWait #第一种情况使用lambda函数,如果未发现定位元素返回自定义的错误信息 kk = WebDriverWait(driver,10).until(lambda driver:driver.find_element_by_id("kw"),message="worry!") kk.send_keys("测试") #第二种情况使用len判断返回要查找的元素是否大于等于1,如果大于等于1则执行接下来的代码 WebDriverWait(self.driver,15).until(lambda x:len(self.driver.find_element_by_id("image_cancel"))>=1) self.driver.find_element_by_id("image_cancel").click() #第三种情况使用expected_conditions判断元素是否可见,如果可见则执行接下来的代码 WebDriverWait(self.driver,15).until(expected_conditions.visibility_of_element_located((By.ID,"image_cancel"))) self.driver.find_element_by_id("image_cancel").click() #第四种情况使用自定义函数的形式,灵活度自己掌握 def loaded(driver): print(datetime.now()) if len(self.driver.find_element_by_id("image_cancel"))>=1: self.driver.find_element_by_id("image_cancel").click() return True else: return False WebDriverWait(self.driver, 15).until(loaded)
3、强制等待
time.sleep(20)
六、Toast识别
toast练习的apk下载地址:https://github.com/appium/java-client/blob/master/src/test/resources/apps/ApiDemos-debug.apk
toast捕获必须使用xpath查找,练习代码示例
def test_toast(self): self.driver.find_element_by_accessibility_id('Views').click() self.driver.swipe(350,1000,350,300) #也可以用uiautomator方式 self.driver.find_element_by_accessibility_id('Popup Menu').click() """ find_element_by_accessibility_id与find_element(MobileBy.ACCESSIBILITY_ID,"Make a Popup!")等效 """ self.driver.find_element_by_accessibility_id('Make a Popup!').click() # self.driver.find_element(MobileBy.ACCESSIBILITY_ID,"Make a Popup!").click() self.driver.find_element_by_xpath('//*[@text="Search"]').click() # self.driver.find_element(MobileBy.XPATH,'//*[@text="Search"]').click() #toast第一种识别方法,用class # print(self.driver.find_element_by_xpath("//*[@class='android.widget.Toast']").text) #toast第二种识别方法,用text文本 print(self.driver.find_element_by_xpath("//*[contains(@text,'Clicked popup menu item Search')]").text)
七、断言
元素查找:find_elements
元素属性:get_attribute
1 from appium import webdriver 2 import time 3 from unittest import TestCase 4 from hamcrest import * 5 6 class TestDemo(TestCase): 7 def setUp(self): 8 caps = {} 9 caps["platformName"] = "Android" 10 caps["deviceName"] = "Android Emulator" 11 caps["appPackage"] = "com.xxxx.android" 12 caps["appActivity"] = ".view.WelcomeActivityAlias" 13 caps["autoGrantPermissions"] = "true" 14 15 self.driver = webdriver.Remote("http://localhost:4723/wd/hub", caps) 16 self.driver.implicitly_wait(20) 17 #打开APP首页时同意内容 18 el2 = self.driver.find_element_by_id("com.xxxx.android:id/tv_agree") 19 el2.click() 20 21 def test_search(self): 22 el3 = self.driver.find_element_by_id("com.xxxx.android:id/home_search") 23 el3.click() 24 el4 = self.driver.find_element_by_id("com.xxxx.android:id/search_input_text") 25 el4.send_keys("拼多多") 26 self.driver.find_element_by_id('name').click() 27 price = self.driver.find_element_by_id("current_price") 28 #断言拼多多的股价是否大于68.5元 29 assert float(price.text) > 68.5 30 #断言price的元素属性resource-id是否为price 31 assert "price" in price.get_attribute("resource-id") 32 #hamcrest方式断言price元素属性的package包名是否为xueqiu.android 33 assert_that(price.get_attribute("package"),equal_to("com.xxxx.android")) 34 35 def tearDown(self): 36 time.sleep(3) 37 self.driver.quit()
八、参数化与数据驱动
1、利用pytest进行简单的参数化
pytest和unittest编写上的两点不同:1、类名中是否写TestCase;2、pytest中setup和teardown是小写,而unittest中是大写
1 import pytest,time 2 from appium import webdriver 3 4 class TestDemo(): 5 def setup(self): 6 caps = {} 7 caps["platformName"] = "Android" 8 caps["deviceName"] = "Android Emulator" 9 caps["appPackage"] = "com.xxxx.android" 10 caps["appActivity"] = ".view.WelcomeActivityAlias" 11 caps["autoGrantPermissions"] = "true" 12 13 self.driver = webdriver.Remote("http://localhost:4723/wd/hub", caps) 14 self.driver.implicitly_wait(20) 15 self.driver.find_element_by_id("com.xxxx.android:id/tv_agree").click() 16 17 #要进行参数化的数据 18 @pytest.mark.parametrize("keyword,expected_price", [ 19 ("拼多多", 68.5), 20 ("京东", 49) 21 ]) 22 def test_search(self,keyword,expected_price): 23 self.driver.find_element_by_id("com.xxxx.android:id/home_search").click() 24 self.driver.find_element_by_id("com.xxxx.android:id/search_input_text").send_keys(keyword) 25 self.driver.find_element_by_id('name').click() 26 price = self.driver.find_element_by_id("current_price") 27 assert float(price.text) > expected_price 28 29 def teardown(self): 30 time.sleep(3) 31 self.driver.quit()
2、数据驱动
search.yaml数据文件及运行代码实现参数化数据读取来自外部文件
1 - [ pdd, 68.5 ] 2 - [ jd, 48 ]
1 import pytest,time 2 import yaml 3 from appium import webdriver 4 5 class TestDemo(): 6 search_data = yaml.safe_load(open("search.yaml",'r')) 7 print(search_data) 8 def setup(self): 9 caps = {} 10 caps["platformName"] = "Android" 11 caps["deviceName"] = "Android Emulator" 12 caps["appPackage"] = "com.xxxx.android" 13 caps["appActivity"] = ".view.WelcomeActivityAlias" 14 caps["autoGrantPermissions"] = "true" 15 16 self.driver = webdriver.Remote("http://localhost:4723/wd/hub", caps) 17 self.driver.implicitly_wait(20) 18 self.driver.find_element_by_id("com.xxxx.android:id/tv_agree").click() 19 20 @pytest.mark.parametrize("keyword,expected_price", search_data) 21 def test_search(self,keyword,expected_price): 22 self.driver.find_element_by_id("com.xxxx.android:id/home_search").click() 23 self.driver.find_element_by_id("com.xxxx.android:id/search_input_text").send_keys(keyword) 24 self.driver.find_elements_by_id('name')[0].click() 25 price = self.driver.find_element_by_id("current_price") 26 assert float(price.text) > expected_price 27 28 def teardown(self): 29 time.sleep(3) 30 self.driver.quit()
testcase.yaml测试步骤读取外部文件
1 - id: home_search 2 - id: search_input_text 3 input: pdd 4 - id: name 5 - id: current_price 6 get: text
1 import yaml,time 2 from appium import webdriver 3 from selenium.webdriver.remote.webdriver import WebDriver 4 5 class TestDemo(): 6 def setup(self): 7 caps = {} 8 caps["platformName"] = "Android" 9 caps["deviceName"] = "Android Emulator" 10 caps["appPackage"] = "com.xxxx.android" 11 caps["appActivity"] = ".view.WelcomeActivityAlias" 12 caps["autoGrantPermissions"] = "true" 13 14 self.driver = webdriver.Remote("http://localhost:4723/wd/hub", caps) 15 self.driver.implicitly_wait(20) 16 self.driver.find_element_by_id("com.xxxx.android:id/tv_agree").click() 17 18 def test_search(self): 19 Test_Case("testcase.yaml").run(self.driver) 20 21 def teardown(self): 22 time.sleep(3) 23 self.driver.quit() 24 25 class Test_Case: 26 def __init__(self,path): 27 file = open(path,"r") 28 self.steps = yaml.safe_load(file) 29 print("打印出来的yaml文件内容为:",self.steps) 30 31 def run(self,driver: WebDriver): 32 for step in self.steps: 33 if isinstance(step, dict): 34 element = None 35 if "id" in step.keys(): 36 element = driver.find_element_by_id(step["id"]) 37 elif "xpat" in step.keys(): 38 element = driver.find_element_by_xpath(step["xpath"]) 39 else: 40 print(step.keys()) 41 42 if "input" in step.keys(): 43 element.send_keys(step["input"]) 44 elif "get" in step.keys(): 45 text = element.get_attribute(step["get"]) 46 print("获取到的文本内容为:", text) 47 else: 48 element.click()