天天看點

python主講移動端自動化測試架構appium_python+appium+yaml移動端自動化測試架構實作詳解...

結構介紹

之前分享過一篇安卓UI測試,但是沒有實作資料與代碼分離,後期維護成本較高,是以最近抽空優化了一下。

不想看文章得可以直接去Github,歡迎拍磚

大緻結構如下:

python主講移動端自動化測試架構appium_python+appium+yaml移動端自動化測試架構實作詳解...

testyaml管理用例,實作資料與代碼分離,一個子產品一個檔案夾

public 存放公共檔案,如讀取配置檔案、啟動appium服務、讀取Yaml檔案、定義日志格式等

page 存放最小測試用例集,一個子產品一個檔案夾

results 存放測試報告及失敗截圖

python主講移動端自動化測試架構appium_python+appium+yaml移動端自動化測試架構實作詳解...

logs 存放日志

python主講移動端自動化測試架構appium_python+appium+yaml移動端自動化測試架構實作詳解...
python主講移動端自動化測試架構appium_python+appium+yaml移動端自動化測試架構實作詳解...

testcase 存放測試用例runtest.py 運作所有測試用例

yaml格式介紹

首先看下yaml檔案的格式,之前也寫過一點關于yaml文法學習的文章

testcase部分是重點,其中:

element_info:定位元素資訊

find_type:屬性,id、xpath、text、ids

operate_type: click、sendkeys、back、swipe_up 為back就是傳回,暫時就四種

上面三個必填,operate_type必填!!!!!!

send_content:send_keys 時用到

index:ids時用到

times: 傳回次數或者上滑次數

testinfo:

- id: cm001

title: 新增終端門店

execute: 1

testcase:

-

element_info: 客戶

find_type: text

operate_type: click

-

element_info: com.fiberhome.waiqin365.client:id/cm_topbar_tv_right

find_type: id

operate_type: click

-

element_info: com.fiberhome.waiqin365.client:id/custview_id_singletv_inputtext

find_type: ids

operate_type: send_keys

send_content: auto0205

index: 0

-

element_info:

find_type:

operate_type: swipe_up

times: 1

-

element_info: 送出

find_type: text

operate_type: click

-

element_info:

find_type:

operate_type: back

times: 1

代碼部分

公共部分

個人覺得核心的就是公共部分,相當于建房子,公共部分搞好了,後面僅僅是調用即可,建房子把架子搭好,後面就添磚加瓦吧。

讀取配置檔案readconfig.py

設定日志格式logs.py

擷取裝置GetDevices.py

這幾個通用的就不做介紹了

讀取yaml檔案 GetYaml.py

主要用來讀取yaml檔案

#coding=utf-8

#author='Shichao-Dong'

import sys

reload(sys)

sys.setdefaultencoding('utf8')

import yaml

import codecs

class getyaml:

def __init__(self,path):

self.path = path

def getYaml(self):

'''

讀取yaml檔案

:param path: 檔案路徑

:return:

'''

try:

f = open(self.path)

data =yaml.load(f)

f.close()

return data

except Exception:

print(u"未找到yaml檔案")

def alldata(self):

data =self.getYaml()

return data

def caselen(self):

data = self.alldata()

length = len(data['testcase'])

return length

def get_elementinfo(self,i):

data = self.alldata()

# print data['testcase'][i]['element_info']

return data['testcase'][i]['element_info']

def get_findtype(self,i):

data = self.alldata()

# print data['testcase'][i]['find_type']

return data['testcase'][i]['find_type']

def get_operate_type(self,i):

data = self.alldata()

# print data['testcase'][i]['operate_type']

return data['testcase'][i]['operate_type']

def get_index(self,i):

data = self.alldata()

if self.get_findtype(i)=='ids':

return data['testcase'][i]['index']

else:

pass

def get_send_content(self,i):

data = self.alldata()

# print data['testcase'][i]['send_content']

if self.get_operate_type(i) == 'send_keys':

return data['testcase'][i]['send_content']

else:

pass

def get_backtimes(self,i):

data = self.alldata()

if self.get_operate_type(i)=='back' or self.get_operate_type(i)=='swipe_up':

return data['testcase'][i]['times']

else:

pass

def get_title(self):

data = self.alldata()

# print data['testinfo'][0]['title']

return data['testinfo'][0]['title']

啟動appium服務 StartAppiumServer.py

主要是啟動appium并傳回端口port,這個port在下面的driver中需要

#coding=utf-8

#author='Shichao-Dong'

from logs import log

import random,time

import platform

import os

from GetDevices import devices

log = log()

dev = devices().get_deviceName()

class Sp:

def __init__(self, device):

self.device = device

def __start_driver(self, aport, bpport):

"""

:return:

"""

if platform.system() == 'Windows':

import subprocess

subprocess.Popen("appium -p %s -bp %s -U %s" %

(aport, bpport, self.device), shell=True)

def start_appium(self):

"""

啟動appium

p:appium port

bp:bootstrap port

:return: 傳回appium端口參數

"""

aport = random.randint(4700, 4900)

bpport = random.randint(4700, 4900)

self.__start_driver(aport, bpport)

log.info(

'start appium :p %s bp %s device:%s' %

(aport, bpport, self.device))

time.sleep(10)

return aport

def main(self):

"""

:return: 啟動appium

"""

return self.start_appium()

def stop_appium(self):

'''

停止appium

:return:

'''

if platform.system() == 'Windows':

os.popen("taskkill /f /im node.exe")

if __name__ == '__main__':

s = Sp(dev)

s.main()

擷取driver GetDriver.py

platformName、deviceName、appPackage、appActivity這些解除安裝配置檔案config.ini檔案中,可以直接通過readconfig.py檔案讀取獲得。

appium_port有StartAppiumServer.py檔案傳回

s = Sp(deviceName)

appium_port = s.main()

def mydriver():

desired_caps = {

'platformName':platformName,'deviceName':deviceName, 'platformVersion':platformVersion,

'appPackage':appPackage,'appActivity':appActivity,

'unicodeKeyboard':True,'resetKeyboard':True,'noReset':True

}

try:

driver = webdriver.Remote('http://127.0.0.1:%s/wd/hub'%appium_port,desired_caps)

time.sleep(4)

log.info('擷取driver成功')

return driver

except WebDriverException:

print 'No driver'

if __name__ == "__main__":

mydriver()

重新封裝find等指令,BaseOperate.py

裡面主要是一些上滑、傳回、find等一些基礎操作

#coding=utf-8

#author='Shichao-Dong'

from selenium.webdriver.support.ui import WebDriverWait

from logs import log

import os

import time

'''

一些基礎操作:滑動、截圖、點選頁面元素等

'''

class BaseOperate:

def __init__(self,driver):

self.driver = driver

def back(self):

'''

傳回鍵

:return:

'''

os.popen("adb shell input keyevent 4")

def get_window_size(self):

'''

擷取螢幕大小

:return: windowsize

'''

global windowSize

windowSize = self.driver.get_window_size()

return windowSize

def swipe_up(self):

'''

向上滑動

:return:

'''

windowsSize = self.get_window_size()

width = windowsSize.get("width")

height = windowsSize.get("height")

self.driver.swipe(width/2, height*3/4, width/2, height/4, 1000)

def screenshot(self):

now=time.strftime("%y%m%d-%H-%M-%S")

PATH = lambda p: os.path.abspath(

os.path.join(os.path.dirname(__file__), p)

)

screenshoot_path = PATH('../results/screenshoot/')

self.driver.get_screenshot_as_file(screenshoot_path+now+'.png')

def find_id(self,id):

'''

尋找元素

:return:

'''

exsit = self.driver.find_element_by_id(id)

if exsit :

return True

else:

return False

def find_name(self,name):

'''

判斷頁面是否存在某個元素

:param name: text

:return:

'''

findname = "//*[@text='%s']"%(name)

exsit = self.driver.find_element_by_xpath(findname)

if exsit :

return True

else:

return False

def get_name(self,name):

'''

定位頁面text元素

:param name:

:return:

'''

# element = driver.find_element_by_name(name)

# return element

findname = "//*[@text='%s']"%(name)

try:

element = WebDriverWait(self.driver, 10).until(lambda x: x.find_element_by_xpath(findname))

# element = self.driver.find_element_by_xpath(findname)

self.driver.implicitly_wait(2)

return element

except:

self.screenshot()

log.error('未定位到元素:'+'%s')%(name)

def get_id(self,id):

'''

定位頁面resouce-id元素

:param id:

:return:

'''

try:

element = WebDriverWait(self.driver, 10).until(lambda x: x.find_element_by_id(id))

# element = self.driver.find_element_by_id(id)

self.driver.implicitly_wait(2)

return element

except:

self.screenshot()

log.error('未定位到元素:'+'%s')%(id)

def get_xpath(self,xpath):

'''

定位頁面xpath元素

:param id:

:return:

'''

try:

element = WebDriverWait(self.driver, 10).until(lambda x: x.find_element_by_xpath(xpath))

# element = self.driver.find_element_by_xpath(xpath)

self.driver.implicitly_wait(2)

return element

except:

self.screenshot()

log.error('未定位到元素:'+'%s')%(xpath)

def get_ids(self,id):

'''

定位頁面resouce-id元素組

:param id:

:return:清單

'''

try:

# elements = self.driver.find_elements_by_id(id)

elements = WebDriverWait(self.driver, 10).until(lambda x: x.find_elements_by_id(id))

self.driver.implicitly_wait(2)

return elements

except:

self.screenshot()

log.error('未定位到元素:'+'%s')%(id)

def page(self,name):

'''

傳回至指定頁面

:return:

'''

i=0

while i<10:

i=i+1

try:

findname = "//*[@text='%s']"%(name)

self.driver.find_element_by_xpath(findname)

self.driver.implicitly_wait(2)

break

except :

os.popen("adb shell input keyevent 4")

try:

findname = "//*[@text='确定']"

self.driver.find_element_by_xpath(findname).click()

self.driver.implicitly_wait(2)

except:

os.popen("adb shell input keyevent 4")

try:

self.driver.find_element_by_xpath("//*[@text='工作台']")

self.driver.implicitly_wait(2)

break

except:

os.popen("adb shell input keyevent 4")

Operate.py

我認為最關鍵的一步了,後面沒有page都是調用這個檔案進行測試,主要是根據讀取的yaml檔案,然後進行if...else...判斷,根據對應的operate_type分别進行對應的click、sendkeys等操作

#coding=utf-8

#author='Shichao-Dong'

from GetYaml import getyaml

from BaseOperate import BaseOperate

class Operate:

def __init__(self,path,driver):

self.path = path

self.driver = driver

self.yaml = getyaml(self.path)

self.baseoperate=BaseOperate(driver)

def check_operate_type(self):

'''

讀取yaml資訊并執行

element_info:定位元素資訊

find_type:屬性,id、xpath、text、ids

operate_type: click、sendkeys、back、swipe_up 為back就是傳回,暫時就三種

上面三個必填,operate_type必填!!!!!!

send_content:send_keys 時用到

index:ids時用到

times:

:return:

'''

for i in range(self.yaml.caselen()):

if self.yaml.get_operate_type(i) == 'click':

if self.yaml.get_findtype(i) == 'text':

self.baseoperate.get_name(self.yaml.get_elementinfo(i)).click()

elif self.yaml.get_findtype(i) == 'id':

self.baseoperate.get_id(self.yaml.get_elementinfo(i)).click()

elif self.yaml.get_findtype(i) == 'xpath':

self.baseoperate.get_xpath(self.yaml.get_elementinfo(i)).click()

elif self.yaml.get_findtype(i) == 'ids':

self.baseoperate.get_ids(self.yaml.get_elementinfo(i))[self.yaml.get_index(i)].click()

elif self.yaml.get_operate_type(i) == 'send_keys':

if self.yaml.get_findtype(i) == 'text':

self.baseoperate.get_name(self.yaml.get_elementinfo(i)).send_keys(self.yaml.get_send_content(i))

elif self.yaml.get_findtype(i) == 'id':

self.baseoperate.get_id(self.yaml.get_elementinfo(i)).send_keys(self.yaml.get_send_content(i))

elif self.yaml.get_findtype(i) == 'xpath':

self.baseoperate.get_xpath(self.yaml.get_elementinfo(i)).send_keys(self.yaml.get_send_content(i))

elif self.yaml.get_findtype(i) == 'ids':

self.baseoperate.get_ids(self.yaml.get_elementinfo(i))[self.yaml.get_index(i)].send_keys(self.yaml.get_send_content(i))

elif self.yaml.get_operate_type(i) == 'back':

for n in range(self.yaml.get_backtimes(i)):

self.baseoperate.back()

elif self.yaml.get_operate_type(i) == 'swipe_up':

for n in range(self.yaml.get_backtimes(i)):

self.baseoperate.swipe_up()

def back_home(self):

'''

傳回至工作台

:return:

'''

self.baseoperate.page('工作台')

公共部分的代碼就介紹這麼多,在編寫這個架構的時候,大部分精力都花在這部分,是以個人覺得還是值得好好研究的

Page部分

page部分是最小用例集,一個子產品一個檔案夾,以客戶為例,

目前寫了兩個用例,一個新增,一個排序,檔案如下:

python主講移動端自動化測試架構appium_python+appium+yaml移動端自動化測試架構實作詳解...

代碼如下,非常的簡潔,

import sys

reload(sys)

sys.setdefaultencoding('utf8')

import codecs,os

from public.Operate import Operate

from public.GetYaml import getyaml

PATH = lambda p: os.path.abspath(

os.path.join(os.path.dirname(__file__), p)

)

yamlpath = PATH("../../testyaml/cm/cm-001addcm.yaml")

class AddcmPage:

def __init__(self,driver):

self.path = yamlpath

self.driver = driver

self.operate = Operate(self.path,self.driver)

def operateap(self):

self.operate.check_operate_type()

def home(self):

self.operate.back_home()

運作用例

這部分用了unittest,運作所有測試用例和生成報告。

一個子產品一個用例,以客戶為例:CmTest.py

from page.cm.CmAddcmPage import AddcmPage

from page.cm.CmSortcmPage import SortcmPage

from public.GetDriver import mydriver

driver = mydriver()

import unittest,time

class Cm(unittest.TestCase):

def test_001addcm(self):

'''

新增客戶

:return:

'''

add = AddcmPage(driver)

add.operateap()

add.home()

def test_002sortcm(self):

'''

客戶排序

:return:

'''

sort = SortcmPage(driver)

sort.sortlist()

sort.home()

def test_999close(self):

driver.quit()

time.sleep(10)

if __name__ == "__main__":

unittest.main()

首先從page層将需要運作的用例都import進來,然後用unittest運作即可。

如果想要運作所有的測試用例,需要用到runtest.py

import time,os

import unittest

import HTMLTestRunner

from testcase.CmTest import Cm

def testsuit():

suite = unittest.TestSuite()

suite.addTests([unittest.defaultTestLoader.loadTestsFromTestCase(Cm),

])

# runner = unittest.TextTestRunner(verbosity=2)

# runner.run(suite)

now=time.strftime("%y-%m-%d-%H-%M-%S")

PATH = lambda p: os.path.abspath(

os.path.join(os.path.dirname(__file__), p)

)

dirpath = PATH("./results/waiqin365-")

filename=dirpath + now +'result.html'

fp=open(filename,'wb')

runner=HTMLTestRunner.HTMLTestRunner(stream=fp,title='waiqin365 6.0.6beta test result',description=u'result:')

runner.run(suite)

fp.close()

if __name__ =="__main__":

testsuit()

這邊的思路差不多,也是先導入再裝入suite即可

總結

就目前而言,暫時算是實作了資料與用例的分離,但是yaml的編寫要求較高,不能格式上出錯。

同時也有一些其他可以優化的地方,如:

對彈窗的判斷

斷開後重連機制

失敗後重跑機制

到此這篇關于python+appium+yaml移動端自動化測試架構實作詳解的文章就介紹到這了,更多相關python appium yaml 自動化測試 内容請搜尋腳本之家以前的文章或繼續浏覽下面的相關文章希望大家以後多多支援腳本之家!