天天看點

python主講移動端自動化測試架構appium 吾愛破解_appium移動自動化測試架構搭建實戰,附源碼(一)...

最近搭建了一個安卓端的APP自動化測試架構,下面就總結一些搭建的過程和思路,有不足之處還請指出

1、首先說明一下環境:

編輯器:pycharm2018.3.2

python環境:python3.6

appium環境:appium V1.15.1

另外還有生成報告用到的allure

2、再給大家看一下架構結構:

python主講移動端自動化測試架構appium 吾愛破解_appium移動自動化測試架構搭建實戰,附源碼(一)...

base裡面放的公用的方法,比如find_element,click,sendKeys等;

data裡面放的是我的測試用例所用到的一些參數,yml檔案

files裡面就是待測試的apk,測試用例,測試計劃等,我這裡還放了我測試過程需要上傳的圖檔

page和scripts是PO模式,page放的某頁面中的方法,scripts放的測試用例

result放的測試結果的log和報告

screen是放我測試過程中的一些截圖的

util和base功能一樣,天知道我為什麼弄兩個。。其實這兩個檔案夾合并也是可以的。

下面就是挨個檔案夾介紹了。

3、因為很多地方用到了util和base裡面的東西,是以我們先說這兩個

util:

python主講移動端自動化測試架構appium 吾愛破解_appium移動自動化測試架構搭建實戰,附源碼(一)...

先說一下log.py,是用來記錄log的,下面貼一下記錄log的代碼,這段代碼嚴格來說不是我寫的,之前看到一個公衆号,感覺還不錯,就拿過來改吧改吧用了,

import logging

from datetime importdatetimeimportosimportthreadingclassLog:def __init__(self):

self.pro_dir= os.path.dirname(os.path.abspath(__file__))

self.pro_dir=os.path.split(self.pro_dir)[0]

# 下面是記錄log的檔案建立的過程

self.result_path= os.path.join(self.pro_dir, "result")if notos.path.exists(self.result_path):

os.mkdir(self.result_path)

self.log_path= os.path.join(self.result_path, str(datetime.now().strftime("%Y%m%d%H%M%S")))if notos.path.exists(self.log_path):

os.mkdir(self.log_path)

self.logger=logging.getLogger()

self.logger.setLevel(logging.INFO)#建立處理器對象

handler = logging.FileHandler(os.path.join(self.log_path, "output.log"))

formatter= logging.Formatter('%(levelname)s %(name)s:%(filename)s:%(lineno)s>> %(message)s')#為處理器添加設定格式器對象,添加過濾器對象的方法為:handler.setFilter(filter)

handler.setFormatter(formatter)

self.logger.addHandler(handler)defget_logger(self):returnself.loggerclassMyLog:"""将上面的記錄log的方法放到一個線程内,讓它單獨啟用一個線程,是為了更好的寫log"""log=None

mutex=threading.Lock()def __init__(self):[email protected]_log():if MyLog.log isNone:

MyLog.mutex.acquire()

MyLog.log=Log()

MyLog.mutex.release()return MyLog.log

check_devices是用來判斷手機有沒有連接配接上,以及有沒有安裝需要測試的APP

建立driver的時候,首先判斷了手機有沒有連接配接上,接着判斷APP有沒有安裝,如果沒有安裝,再确認一下apk有沒有,有的話就自動安裝,安裝完再測試。是以用到了下面這堆

importglobimportosfrom base.base_action importBaseActionfrom util.log importMyLog#定義全局變量

devices_list_finally =[]

chose_file_num=[]

log=MyLog().get_log()

logger=log.get_logger()defis_devices_link():"""檢查是否有裝置連接配接PC,有則傳回True

:return:"""devices_list_start=[]

devices_cmd= os.popen('adb devices').readlines()

devices_list_start_count=len(devices_cmd)

devices_list_start_count= devices_list_start_count - 2

if devices_list_start_count >= 1:print('find devices linked')for devices_num inrange(devices_list_start_count):

devices_list_start.append(devices_cmd[devices_num+ 1])

device_list_pers= devices_list_start[devices_num].index('\t')

devices_list_finally.append(devices_list_start[devices_num][:device_list_pers])print('devices list :' + '%d' % (devices_num + 1) + '%s' %devices_list_finally[devices_num])returnTrueelse:print('Can not find devices link...pls check device link...')

logger.error("無法連接配接到手機,試試重新插拔手機")returnFalsedefis_apk_installed(apk_path):"""判斷手機是否安裝了待測試APP,安裝則傳回True

:return:"""app_package=BaseAction.get_app_package(apk_path)

app_package= 'package:' + app_package + '\n'all_packages= list(os.popen("adb shell pm list package"))if app_package inall_packages:returnTrueelse:returnFalse#檢查本地檔案是否存在,這個檔案放到了files檔案夾下的apk檔案夾裡面

defcheck_local_file(apk_path):

file_list=glob.glob(apk_path)

file_index=len(file_list)if file_index !=0:if file_index == 1:returnTrueelse:

logger.error("無法安裝APP,請檢查apk檔案路徑是否正确")

exit()#安裝應用

definstall_apk(apk_path):for install_apk_to_devices_index inrange(len(devices_list_finally)):

os.system('adb -s' + ' ' + devices_list_finally[install_apk_to_devices_index] + ' ' + 'install' + ' ' + apk_path)

GlobalVar.py檔案,寫來是因為有的case需要跨檔案設定全局變量,是以有了這個檔案:

"""定義全局變量,并且全局變量需要跨檔案使用時,可以用該類。

比如定義全局變量的時候可以這樣:

global_var = GlobalVar()

global_var.set_value("name", "value")

使用該全局變量的時候這樣:

global_var.get_value("name")"""

classGlobalVar:def __init__(self):global_global_dict

_global_dict={}

@staticmethoddefset_value(name, value):

_global_dict[name]=value

@staticmethoddef get_value(name, def_value=None):try:return_global_dict[name]exceptKeyError:return def_value

readConfig就是讀取配置檔案的方法:

"""讀取配置檔案的各種方法"""

importcodecsimportconfigparserimportosfrom selenium.webdriver.common.by importByfrom util.log importMyLog

log=MyLog().get_log()

logger=log.get_logger()defdir_log(test):"""捕獲異常的裝飾器方法

:param test:

:return:"""

def log(*args, **kwargs):try:

res= test(*args, **kwargs)returnresexceptException:raise

returnlogclassReadConfig:

project_dir= os.path.dirname(os.path.abspath(__file__))

project_dir=os.path.split(project_dir)[0]def __init__(self, config_path="config.ini"):#需要讀取的配置檔案路徑

self.config_path =os.path.join(self.project_dir, config_path)try:

with open(self.config_path, encoding="UTF-8") as fd:

data=fd.read()#判斷data是否帶BOM,如果帶就删除

if data[:3] ==codecs.BOM_UTF8:

data= data[3:]#使用codecs.open打開檔案,寫入的時候更不容易出現編碼問題,open方法隻能寫入str

with codecs.open(self.config_path, "w", encoding="UTF-8") as file:

file.write(data)exceptFileNotFoundError as e:#logging.error(str(e))

print(e)#将配置檔案分割成一塊一塊的字典形式

self.cfp =configparser.ConfigParser()

self.cfp.read(self.config_path, encoding="UTF-8")

@dir_logdefget_db(self, name):

value= self.cfp.get("DATABASE", name)returnvalue

@dir_logdefget_test(self, name):

value= self.cfp.get("TEST", name)return value

接下來是讀取資料庫的方法:

#encoding=utf-8

"""讀取資料庫的方法"""

importpymysqlfrom util.read_config importReadConfigfrom util.log importMyLogclassMyDB(object):def __init__(self):

self.log=MyLog.get_log()

self.logger=self.log.get_logger()

local_read_config=ReadConfig()

host= local_read_config.get_db("host")

username= local_read_config.get_db("username")

password= local_read_config.get_db("password")

port= local_read_config.get_db("port")

database= local_read_config.get_db("database")

self.config={'host': str(host),'user': username,'password': password,'port': int(port),'db': database

}

self.db=None

self.cursor=None

@classmethoddef __new__(cls, *args, **kwargs):"""每一次執行個體化的時候,都傳回同一個instance對象"""

if not hasattr(cls, "_instance"):

cls._instance= super(MyDB, cls).__new__(cls)returncls._instancedefconnect_db(self):try:

self.db= pymysql.connect(**self.config)

self.cursor=self.db.cursor()

self.logger.info("連接配接資料庫成功")exceptConnectionError as ex:

self.logger.error(str(ex))def execute_sql(self, sql, params=None):

self.connect_db()

self.cursor.execute(sql, params)

self.db.commit()returnself.cursordefget_all(self, cur):

value=cur.fetchall()returnvaluedefclose_db(self):

self.db.close()

self.logger.info("關閉資料庫")

上面這些涉及到了讀取配置檔案的東西,是以把 config.ini檔案貼一下:

python主講移動端自動化測試架構appium 吾愛破解_appium移動自動化測試架構搭建實戰,附源碼(一)...

上面的TEL裡面的内容是連接配接手機用到的

[DATABASE]下面是連接配接資料庫相關的資訊

其餘的内容有時間再更新~~~