Appium(一):appium環(huán)境以及基礎(chǔ)函數(shù)的封裝

一:搭建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:


1.png

進(jìn)到如下這個(gè)界面,然后點(diǎn)擊頁面右上角的放大鏡:


3.png

進(jìn)到appium-server的基礎(chǔ)信息設(shè)置界面搀别,點(diǎn)擊Saved Capability Sets:
2.png

這里面可以配置物理機(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窗口輸出的日志


4.png

可以看到輸出的日志中颠黎,有一個(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)為止。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末邻眷,一起剝皮案震驚了整個(gè)濱河市眠屎,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌耗溜,老刑警劉巖组力,帶你破解...
    沈念sama閱讀 212,454評(píng)論 6 493
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異抖拴,居然都是意外死亡燎字,警方通過查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 90,553評(píng)論 3 385
  • 文/潘曉璐 我一進(jìn)店門阿宅,熙熙樓的掌柜王于貴愁眉苦臉地迎上來候衍,“玉大人,你說我怎么就攤上這事洒放◎嚷梗” “怎么了?”我有些...
    開封第一講書人閱讀 157,921評(píng)論 0 348
  • 文/不壞的土叔 我叫張陵往湿,是天一觀的道長妖异。 經(jīng)常有香客問我惋戏,道長,這世上最難降的妖魔是什么他膳? 我笑而不...
    開封第一講書人閱讀 56,648評(píng)論 1 284
  • 正文 為了忘掉前任响逢,我火速辦了婚禮,結(jié)果婚禮上棕孙,老公的妹妹穿的比我還像新娘舔亭。我一直安慰自己,他們只是感情好蟀俊,可當(dāng)我...
    茶點(diǎn)故事閱讀 65,770評(píng)論 6 386
  • 文/花漫 我一把揭開白布钦铺。 她就那樣靜靜地躺著,像睡著了一般肢预。 火紅的嫁衣襯著肌膚如雪矛洞。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 49,950評(píng)論 1 291
  • 那天烫映,我揣著相機(jī)與錄音缚甩,去河邊找鬼。 笑死窑邦,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的壕探。 我是一名探鬼主播冈钦,決...
    沈念sama閱讀 39,090評(píng)論 3 410
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢(mèng)啊……” “哼李请!你這毒婦竟也來了瞧筛?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 37,817評(píng)論 0 268
  • 序言:老撾萬榮一對(duì)情侶失蹤导盅,失蹤者是張志新(化名)和其女友劉穎较幌,沒想到半個(gè)月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體白翻,經(jīng)...
    沈念sama閱讀 44,275評(píng)論 1 303
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡乍炉,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 36,592評(píng)論 2 327
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了滤馍。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片岛琼。...
    茶點(diǎn)故事閱讀 38,724評(píng)論 1 341
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖巢株,靈堂內(nèi)的尸體忽然破棺而出槐瑞,到底是詐尸還是另有隱情,我是刑警寧澤阁苞,帶...
    沈念sama閱讀 34,409評(píng)論 4 333
  • 正文 年R本政府宣布困檩,位于F島的核電站祠挫,受9級(jí)特大地震影響,放射性物質(zhì)發(fā)生泄漏悼沿。R本人自食惡果不足惜等舔,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 40,052評(píng)論 3 316
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望显沈。 院中可真熱鬧软瞎,春花似錦、人聲如沸拉讯。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,815評(píng)論 0 21
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽魔慷。三九已至只锭,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間院尔,已是汗流浹背蜻展。 一陣腳步聲響...
    開封第一講書人閱讀 32,043評(píng)論 1 266
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留邀摆,地道東北人纵顾。 一個(gè)月前我還...
    沈念sama閱讀 46,503評(píng)論 2 361
  • 正文 我出身青樓,卻偏偏與公主長得像栋盹,于是被迫代替她去往敵國和親施逾。 傳聞我的和親對(duì)象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 43,627評(píng)論 2 350