python主讲移动端自动化测试框架appium_python+appium+yaml移动端自动化测试框架实现详解...

结构介绍

之前分享过一篇安卓UI测试,但是没有实现数据与代码分离,后期维护成本较高,所以最近抽空优化了一下。

不想看文章得可以直接去Github,欢迎拍砖

大致结构如下:

202011241008591.png

testyaml管理用例,实现数据与代码分离,一个模块一个文件夹

public 存放公共文件,如读取配置文件、启动appium服务、读取Yaml文件、定义日志格式等

page 存放最小测试用例集,一个模块一个文件夹

results 存放测试报告及失败截图

202011241009002.png

logs 存放日志

202011241009003.png

202011241009004.png

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部分是最小用例集,一个模块一个文件夹,以客户为例,

目前写了两个用例,一个新增,一个排序,文件如下:

202011241009005.png

代码如下,非常的简洁,

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 自动化测试 内容请搜索脚本之家以前的文章或继续浏览下面的相关文章希望大家以后多多支持脚本之家!

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值