一:搭建appium環(huán)境
1饥瓷、JDK和Android SDK配置
2槐臀、安裝Node.js
3蜡峰、安裝.net framework
4葡幸、安裝Appium
5最筒、安裝appium - python - client
google教程很多,可以參照網(wǎng)上的教程蔚叨,這里就不贅述了床蜘。最后使用appium-doctor檢測環(huán)境搭建是否ok。
這篇文章主要介紹下基礎(chǔ)函數(shù)的封裝蔑水。
二:讓appium跑起來
打開appium-server(我這里版本是V1.10.0)邢锯,點(diǎn)擊:Start Server:
進(jìn)到如下這個(gè)界面,然后點(diǎn)擊頁面右上角的放大鏡:
進(jìn)到appium-server的基礎(chǔ)信息設(shè)置界面搀别,點(diǎn)擊Saved Capability Sets:
這里面可以配置物理機(jī)或者模擬器的設(shè)備信息丹擎,然后開始一個(gè)session,需要配置的信息如下:
capabilities = {
"platformName": "Android",
"deviceName": "0acfd8d5",
"appActivity": "com.imooc.component.imoocmain.splash.MCSplashActivity",
"appPackage": "cn.com.open.mooc",
"noReset": "true"#在當(dāng)前 session 下不會(huì)重置應(yīng)用的狀態(tài)领曼。默認(rèn)值為 false鸥鹉,設(shè)置為true后,不需要每次運(yùn)行用例都安裝apk(特別是有開屏頁的app庶骄,不需要每次都經(jīng)過開屏頁毁渗,打開app直接進(jìn)入主頁)
}
根據(jù)appium版本不同,可能有些參數(shù)不是必須的单刁,但是能加上就加上灸异,避免出現(xiàn)問題府适。其中deviceName可以通過在cmd(windows)窗口下執(zhí)行adb devices 獲取,前提是要先打開設(shè)備的調(diào)試模式以及windows電腦需要具備adb環(huán)境肺樟;appActivity和appPackage的獲取有很多種方法獲取檐春,這里有兩種情況:第一種是本地有apk包的情況下,那么直接使用命令aapt dump badging XXX.apk(后面是apk包的絕對(duì)路徑)么伯;第二種情況是沒有apk包疟暖,但是模擬器或者真機(jī)上安裝了應(yīng)用,這時(shí)候可以使用adb logcat >c:\log.txt田柔,把a(bǔ)pp的啟動(dòng)日志輸出到本地的log文件俐巴,然后去log文件過濾查找,如何過濾查找關(guān)鍵字硬爆,這個(gè)百度也有欣舵,我一般是過濾START或者cmp關(guān)鍵字,后面一般跟的就是apk的Activity和Package名稱(不推薦使用這個(gè)方法缀磕,因?yàn)槲以诙啻螄L試的過程中遇到華為手機(jī)有一個(gè)坑爹的問題缘圈,就是系統(tǒng)日志默認(rèn)是關(guān)閉的,需要輸入一串命令來打開log開關(guān):*#*#2846579#*#*袜蚕,進(jìn)入projectmenu--后臺(tái)設(shè)置--LOG設(shè)置--LOG開關(guān)--打開糟把,最終以失敗告終);或者使用adb logcat | grep START命令(windows使用findstr過濾)廷没,在cmd窗口輸入這個(gè)命令后糊饱,啟動(dòng)app,然后查看cmd窗口輸出的日志
可以看到輸出的日志中颠黎,有一個(gè)LAUNCHER關(guān)鍵字另锋,后面緊接著flg=0x10200000cmp=cn.com.open.mooc/com.imooc.component.imoocmain.splash.MCSplashActivity中的cn.com.open.mooc就是包名,com.imooc.component.imoocmain.splash.MCSplashActivity就是啟動(dòng)頁的Activity(推薦使用這種方法)狭归。
準(zhǔn)備工作做完之后夭坪,在appium-server的基礎(chǔ)信息設(shè)置界面選擇一個(gè)自己已保存的設(shè)備,然后點(diǎn)擊start session过椎,就可以看到appium-server端有日志輸出室梅,這個(gè)日志很重要,后續(xù)可以作為調(diào)試信息來定位問題疚宇,同時(shí)也能看到手機(jī)也就運(yùn)行起了相應(yīng)的apk(整個(gè)過程都是需要打開調(diào)試模式的)
三亡鼠、頁面滑動(dòng)初級(jí)使用
appium的滑動(dòng)函數(shù)是:driver.swipe(x,y,x1,y1,time)。起始點(diǎn):x,y敷待;終點(diǎn)x1,y1间涵;time是可選參數(shù),左右滑動(dòng)Y坐標(biāo)不變榜揖,X變化勾哩;上下滑動(dòng)X坐標(biāo)不變抗蠢,Y變化。appium中的坐標(biāo)和數(shù)學(xué)的坐標(biāo)不同思劳,appium坐標(biāo)原點(diǎn)在界面左上角迅矛。
滑動(dòng)函數(shù)的封裝:
代碼和數(shù)據(jù)分離可以有效減少由于數(shù)據(jù)變化而維護(hù)代碼的成本,所以封裝一個(gè)獲取手機(jī)屏幕大小和滑動(dòng)的類潜叛,達(dá)到的效果是:只需要告訴程序我想要滑動(dòng)的方向即可秽褒,滑動(dòng)坐標(biāo)的起始點(diǎn)并不需要關(guān)心。
代碼實(shí)現(xiàn):
from appium import webdriver
from time import sleep
capabilities = {
"platformName": "Android",
#"automationName":"Uiautomator2",#獲取toast元素需要的
"deviceName": "0acfd8d5",
"appActivity": "com.imooc.component.imoocmain.splash.MCSplashActivity",
# "appWaitActivity":"cn.com.open.mooc.index.splash.GuideActivity"#真機(jī)啟動(dòng)頁等待
"appPackage": "cn.com.open.mooc",
"noReset": "true"#在當(dāng)前 session 下不會(huì)重置應(yīng)用的狀態(tài)威兜。默認(rèn)值為 false震嫉,設(shè)置為true后,不需要每次運(yùn)行用例都安裝apk
}
class getSizeAndSwipe():
def __init__(self):#構(gòu)造函數(shù)牡属,實(shí)例化類的對(duì)象時(shí)調(diào)用
self.driver = webdriver.Remote("http://127.0.0.1:4723/wd/hub", capabilities)
self.size = self.driver.get_window_size()#獲取屏幕的大小,返回的形式是{'width': 1080, 'height': 1812}
self.width = self.size["width"]#獲取寬
self.height = self.size["height"]#獲取高
def swipeUp(self):
x1 = self.width / 2
y1 = self.height / 10 * 9#獲取高的十分之九的位置
x2 = x1
y2 = self.height / 10 * 1#獲取十分之一的位置
self.driver.swipe(x1,y1,x2,y2)#向上滑動(dòng)x坐標(biāo)不變,y坐標(biāo)變小
def swipeDown(self):
x1 = self.width / 2
y1 = self.height / 10 * 1
x2 = x1
y2 = self.height / 10 * 9
self.driver.swipe(x1, y1, x2, y2)
def swipeRight(self):
x1 = self.width / 10 * 1
y1 = self.height / 2
x2 = self.width / 10 * 9
y2 = y1
self.driver.swipe(x1, y1, x2, y2)
def swipeLeft(self):
x1 = self.width / 10 * 9
y1 = self.height / 2
x2 = self.width / 10 * 1
y2 = y1
self.driver.swipe(x1, y1, x2, y2)
def swipeAction(self,direction):#定義一個(gè)滑動(dòng)函數(shù)扼睬,傳入一個(gè)方向即可
if direction == "up":
self.swipeUp()
elif direction == "down":
self.swipeDown()
elif direction == "left":
self.swipeLeft()
else:
self.swipeRight()
if __name__ == "__main__":
vivoSwipe = getSizeAndSwipe()
vivoSwipe.swipeAction("up")#告知滑動(dòng)的方向即可
四逮栅、元素定位
1、id定位
2窗宇、class_name定位
3措伐、層級(jí)定位
4、uiautomator定位
5军俊、Xpath定位
6侥加、原生頁面和webview頁面切換,以及toast元素獲取
封裝一個(gè)登陸頁面元素定位的類粪躬,每種定位方式封裝為一個(gè)方法担败,代碼實(shí)現(xiàn):
from appium import webdriver
from time import sleep
from selenium.webdriver.support.ui import WebDriverWait
from selenium.webdriver.support import expected_conditions as EC
capabilities = {
"platformName": "Android",
"automationName":"Uiautomator2",#獲取toast元素需要的
"deviceName": "0acfd8d5",
"appActivity": "com.imooc.component.imoocmain.splash.MCSplashActivity",
# "appWaitActivity":"cn.com.open.mooc.index.splash.GuideActivity"#真機(jī)啟動(dòng)頁等待
"appPackage": "cn.com.open.mooc",
"noReset": "true"#在當(dāng)前 session 下不會(huì)重置應(yīng)用的狀態(tài)。默認(rèn)值為 false镰官,設(shè)置為true后提前,不需要每次運(yùn)行用例都安裝apk
}
class handleLoginElement():
def __init__(self):
self.driver = webdriver.Remote("http://127.0.0.1:4723/wd/hub",capabilities)#初始化driver
def goLoginById(self):#id定位的方式登陸
bottoms = self.driver.find_elements_by_id("cn.com.open.mooc:id/lav")#底部的四個(gè)按鈕(首頁,手記泳唠,我的學(xué)習(xí)狈网,賬號(hào)的id屬性是一樣的,先找出所有的笨腥,然后再通過索引查找需要的元素)
count = bottoms[3]#賬號(hào)元素的索引為3
count.click()#做一個(gè)點(diǎn)擊操作
sleep(1)
self.driver.find_element_by_id("cn.com.open.mooc:id/rl_login_before").click()
self.driver.find_element_by_id("cn.com.open.mooc:id/right_text").click()#若是已有賬號(hào)拓哺,那么直接點(diǎn)擊登陸跳轉(zhuǎn)到登陸界面;非首次登陸脖母,不需要這一步士鸥,點(diǎn)擊登陸會(huì)直接跳轉(zhuǎn)到登陸界面
self.driver.find_element_by_id("cn.com.open.mooc:id/accountEdit").clear()#先清理一下默認(rèn)的上次輸入過的文本
self.driver.find_element_by_id("cn.com.open.mooc:id/accountEdit").send_keys("15555555555")#輸入
self.driver.find_element_by_id("cn.com.open.mooc:id/passwordEdit").clear()
self.driver.find_element_by_id("cn.com.open.mooc:id/passwordEdit").send_keys("XXXX")
self.driver.find_element_by_id("cn.com.open.mooc:id/loginLabel").click()#點(diǎn)擊登陸
sleep(2)
def goLoginByClassName(self):#name定位的方式登陸
bottoms = self.driver.find_elements_by_class_name("android.widget.ImageView")
print(len(bottoms))#可以打印看下找到了幾個(gè)name屬性為android.widget.ImageView的元素
count = bottoms[25]
count.click()
sleep(1)
self.driver.find_elements_by_class_name("android.widget.TextView")[0].click()#通過索引來操作元素
#第二次登陸,沒有上面的這一步self.driver.find_element_by_id("cn.com.open.mooc:id/right_text").click()
self.driver.find_elements_by_class_name("android.widget.EditText")[0].clear()
self.driver.find_elements_by_class_name("android.widget.EditText")[0].send_keys("15555555555")
self.driver.find_elements_by_class_name("android.widget.EditText")[1].clear()
self.driver.find_elements_by_class_name("android.widget.EditText")[1].send_keys("XXXX")
self.driver.find_elements_by_class_name("android.widget.TextView")[2].click()
sleep(2)
def goLoginByUiAutomator(self):
bottoms = self.driver.find_elements_by_android_uiautomator('new UiSelector().resourceId("cn.com.open.mooc:id/lav")')
count = bottoms[3]
count.click()
sleep(1)
self.driver.find_elements_by_android_uiautomator('new UiSelector().className("android.widget.TextView")')[0].click()
self.driver.find_element_by_android_uiautomator('new UiSelector().resourceId("cn.com.open.mooc:id/right_text")').click()#若是已有賬號(hào)镶奉,那么直接點(diǎn)擊登陸跳轉(zhuǎn)到登陸界面础淤;非首次登陸崭放,不需要這一步,點(diǎn)擊登陸會(huì)直接跳轉(zhuǎn)到登陸界面
self.driver.find_elements_by_android_uiautomator('new UiSelector().className("android.widget.EditText")')[0].clear()
self.driver.find_elements_by_android_uiautomator('new UiSelector().className("android.widget.EditText")')[0].send_keys("15555555555")
self.driver.find_elements_by_android_uiautomator('new UiSelector().className("android.widget.EditText")')[1].clear()
self.driver.find_elements_by_android_uiautomator('new UiSelector().className("android.widget.EditText")')[1].send_keys("XXXX")
self.driver.find_elements_by_android_uiautomator('new UiSelector().className("android.widget.TextView")')[2].click()
sleep(2)
def goLoginByXpath(self):
# self.driver.find_elements_by_xpath('//*[contains(@text,"賬號(hào)")]')[1].click()#寫法一鸽凶,找到頁面上text屬性包含“賬號(hào)“的元素
# self.driver.find_element_by_xpath('//android.widget.TextView[@text="賬號(hào)"]').click()#寫法二币砂,找到頁面上classname為android.widget.TextView并且text屬性等于”賬號(hào)”的元素
self.driver.find_element_by_xpath('//android.widget.TextView[@text="賬號(hào)"]/../../preceding-sibling::*[@index="2"]').click()#定位頁面上classname為android.widget.TextView并且text等于”賬號(hào)”的元素;/../../代表向上兩個(gè)層級(jí)玻侥,preceding-sibling代表兄弟節(jié)點(diǎn)决摧,意思是找到兄弟節(jié)點(diǎn)下面index=2的元素
def getWebView(self):#原生和H5頁面之間的切換
webview = self.driver.contexts
print(webview)#打印看下當(dāng)前的上下文窗口有幾個(gè),獲取要切換的窗口
for view in webview:
if view == "XXX":#XXX是要切換過去的窗口
self.driver.switch_to.content(view)
break
def getToast(self):
sleep(1)
bottoms = self.driver.find_elements_by_id("cn.com.open.mooc:id/lav") # 底部的四個(gè)按鈕(首頁凑兰,手記掌桩,我的學(xué)習(xí),賬號(hào)的id屬性是一樣的姑食,先找出所有的波岛,然后再通過索引查找需要的元素)
count = bottoms[3] # 賬號(hào)元素的索引為3
count.click() # 做一個(gè)點(diǎn)擊操作
sleep(1)
self.driver.find_elements_by_class_name("android.widget.TextView")[0].click() # 通過索引來操作元素
self.driver.find_element_by_android_uiautomator('new UiSelector().resourceId("cn.com.open.mooc:id/right_text")').click()
self.driver.find_elements_by_class_name("android.widget.EditText")[0].clear()
self.driver.find_elements_by_class_name("android.widget.EditText")[0].send_keys("15555555555")
self.driver.find_elements_by_class_name("android.widget.TextView")[2].click()
self.driver.find_elements_by_class_name("android.widget.EditText")[1].send_keys("123456")
self.driver.find_elements_by_android_uiautomator('new UiSelector().className("android.widget.TextView")')[2].click()
toast_element = ("xpath", "http://*[contains(@text,'登錄密碼錯(cuò)誤')]")#//代表根目錄,*是通配符音半。表示根目錄下所有text屬性包含“登錄密碼錯(cuò)誤”的元素
try:
toast = WebDriverWait(self.driver, 10, 0.1).until(EC.presence_of_element_located(toast_element))
print(toast.text)
except:
print("toast未找到")
if __name__ == "__main__":
handleelement = handleLoginElement()
handleelement.getToast()
其中则拷,獲取toast元素,需要
1曹鸠、Appium1.6.3以上(包括1.6.3)
2煌茬、Android版本在5.0以上
引入了兩個(gè)包:
from selenium.webdriver.support.ui import WebDriverWait
from selenium.webdriver.support import expected_conditions as EC
WebDriverWait用于智能等待;expected_conditions用于直到某個(gè)條件滿足
toast = WebDriverWait(self.driver, 10, 0.1).until(EC.presence_of_element_located(toast_element))
這段代碼的含義是:智能等待10s彻桃,每隔0.1秒檢測一次坛善,直到某個(gè)定位元素出現(xiàn)為止。