最近老板家心情好,不停的给UI加了好多的任务,iOS开发部门的小哥哥见到UI妹妹的眼神由爱转嫌弃,饱含深深地怨气,每个周都会发布一个迭代;回归测试就是一个繁琐又不能忽略的事,没办法就是懒,不想继续点点点,干脆写个弱弱的iOS测试脚本,满足下自己懒得欲望,一番调研,选择了Appium作为iOS的测试框架,当然我没找到更合适的框架了;一周小成,分享出来,开心开心,大佬屠刀轻点,同为菜鸟慢慢吐槽😂
首先,开发iOS测试脚本的最重要前提是你的有一台macOS系统的设备
安装jdk,弱弱的问一句,哪位和开发沾点边的电脑没有jdk;Java -version检查下吧
接下来就是安装node.js,有好多写的很好的博文,不懂的可以移步了解下,检查下您家电脑的node是否安装成功吧
附带的,检查下您家电脑的npm是否可以正常使用吧
安装完成了以上的几个小东西,接下来就是安装我们尊贵的Appium了,Appium有两种,一种是桌面版,一种是命令行模式,这里建议安装桌面版的Appium-desktop,附上下载链接,下载后装即可;
https://github.com/appium/appium-desktop/releases
安装完成即可启动
仅仅是安装完成Appium-desktop可不代表可以您能开始的秀上您的优雅代码,我这这个坑硬是填了整整一天,Appium-desktop始终无法和客户端进行通讯,一直发生各种各样的报错,抑郁症狂犯;
到这里,先需要检查下Appium是否安装了它所需要的类库,最好的方式就是使用其自带的appium-doctor检查下。
在命令行客户端中输入appium-doctor,appium-doctor就会自动检查类库的安装是否完整,当没安装完整是就会在图中的红框部分显示出来,这时你需要根据提示将缺失的类库一一安装,直到不再爆红(以上爆红为安卓测试需要用到的adb和sdk,这里测试iOS,不进行处理)
到这里,Appium-desktop的安装完成,看看他的盛世美颜吧
要想和手机进行通讯,最重要的一环是安装WebDeiverAgent,安装WebDeiverAgent可能需要iOS开发同事的帮忙,但是你如果很了解xcode的使用情况下,可以自行下载,并完成编译,这部分的安装我暂且不做介绍,因为我之前也是根据大神的教程来完成安装的,需要的可以参考大神们的作品;编译完成后模拟器上会WebDriverAgent的图标,点击图标会启动然后结束,很像闪退的效果
之前推荐大家安装desktop版本的原因就是desktop版本可以截取到App UI的结构,配置好即可启动,并读取App的UI结构,点击红框部分进入到UI框架读取界面
配置好需要的desired capabilities,点击启动即可,其中每一项的内容分析会在下一部分一一解析
{
"bundleId": "com.latsen.Pawfit",
"deviceName": "Simulator",
"platformVersion": "14.2",
"platformName": "iOS",
"automationName": "Appium",
"noReset": true,
"udid": "F1B7F19F-C517-4587-8DE5-8EE73B8BBFDF"
}
启动看看结果吧
好的,第一个阶段算是完成了,环境搭建是我觉得这一个开发iOS测试脚本的最难部分,安装期间会遇到各种各样的问题,可以百度仰慕下大神的作品及其解决方案,祝您顺利
这个准备也是需要费点时间的工作,我这里三言两语就说完,但不代表这个步骤很简单,出现的意外可能就会让您前面的努力白费;
准备好您的模拟器或者手机,这里建议使用模拟器测试,手机会因为SDK的问题导致无法连接到手机,启动模拟器;
接下来就是准备好需要测试的APP文件,这个地方需要注意下,如果该APP文件不适配您的电脑,
启动APP请求网络是时APP会闪退,从而无法进行开发,我自然就遇到过,奇怪的就是网上为何都没出现过呢???为此,我花了三瓶可乐来招待开发小哥哥😅;检查下APP是否可以正常在中运行
文档准备,官方的稳定简直就是烂透了,这里建议使用看云的文档,可能需要登录
https://www.kancloud.cn/testerhome/appium_docs_cn/2001597
Appium配置文档
https://appium.io/docs/cn/writing-running-appium/caps/
完成了以上的折磨后,现在可以开启美丽的开发过程了,启动你的编译器吧
pip安装appium的python客户端代码
pip install Appium-Python-Client
from appium import webdriver
my_ios_driver=None
import json
class my_driver(object):
session=None
def __init__(self):
self.session=self.get_my_driver(self.get_deried_cap())
def get_deried_cap(self):
caps = {}
#APP文件在电脑中的位置,当模拟器中安装后,可以去掉该配置
# caps["app"] = "/Users/kefeng/Desktop/Pawfit1230_1.zip"
#将测试APP的包名
caps["bundleId"] = "com.latsen.Pawfit"
#模拟器名称
caps["deviceName"] = "Simulator"
#模拟器系统版本
caps["platformVersion"] = "14.2"
#测试的平台
caps["platformName"] = "iOS"
#测试期间使用的驱动
caps["automationName"] = "XCUITest"
#是否需要重置APP,不需要写False
caps["noReset"] = True
#命令执行的超时时间,这个很重要
caps["newCommandTimeout"]=600
#是否中断回话,并重启app
caps['autoLaunch']=False
#设备的UDID
caps["udid"] = "F1B7F19F-C517-4587-8DE5-8EE73B8BBFDF"
return caps
def get_my_driver(self,caps):
driver=webdriver.Remote("http://localhost:4723/wd/hub", caps)
driver.set_page_load_timeout(50000)
driver.set_script_timeout(50000)
driver.implicitly_wait(10)
# 设置权限,可根据xcrun simctl privacy命令查看
return driver
def disconnect(self):
my_ios_driver.quit()
my_ios_driver=my_driver().session
if __name__ == '__main__':
my_ios_driver.execute_script('mobile: setPermission','io.appium.example',{ 'location-always': 'yes'})
以上注释中提到的几个配置项都非常非常重要,如果配置错误,将无法启动;
#是否中断回话,并重启app caps["newCommandTimeout"]=600 #是否中断回话,并重启app caps['autoLaunch']=False
这两项在测试和开发中相当重要,尤其是caps['autoLaunch']=False,他可以帮助你不用每次重启APP即可进行测试,这在开发中极其的方便,我们并不是每次都想重启APP来进行测试,尤其是后期再testsuit套件测试时;
开发过程中,经常会出现的错误就是元素查找异常,Appium提供了多样的元素获取方式,如下:
根据类名:
#根据类名:
pet_breed=my_ios_driver.find_element_by_class_name('XCUIElementTypeTextField')[
pet_breed=my_ios_driver.find_elements_by_class_name('XCUIElementTypeTextField')[2]
#根据xpath
my_ios_driver.find_element_by_xpath('//XCUIElementTypeButton[@name="Dog"]').click()
my_ios_driver.find_elements_by_xpath('//XCUIElementTypeButton[@name="Dog"]')[0].click()
#根据predicate
my_ios_driver.find_element_by_ios_predicate('label == "Female"').click()
当然还会有其他的,可以根据您的需求来选择,例如我常用的代码截选
当然还会有其他的,可以根据您的需求来选择,例如我常用的代码截选
if type == "SUBSCRIBABLE" and status == "INACTIVE":
# paypal方式使用手动测试
pay_method = "1"
if str(pay_method) == '1':
device_id.clear()
device_id.send_keys(barcode)
self.hide_key_board()
bind_buttom.click()
sleep(5)
# 启动支付流程
# 检查优惠码有效性
code=my_ios_driver.find_element_by_class_name('XCUIElementTypeTextField')
# 检查无效
code.clear()
code.send_keys("@@@@@@@")
self.hide_key_board()
my_ios_driver.find_element_by_xpath('//XCUIElementTypeButton[@name="Buy now"]').click()
sleep(5)
# 检查过期优惠码
code.clear()
code.send_keys("asdfgv")
self.hide_key_board()
my_ios_driver.find_element_by_xpath('//XCUIElementTypeButton[@name="Buy now"]').click()
sleep(5)
# 已经被使用
code.clear()
code.send_keys("qawsde")
self.hide_key_board()
my_ios_driver.find_element_by_xpath('//XCUIElementTypeButton[@name="Buy now"]').click()
# TODO 优惠码支付,请完善
my_ios_driver.find_element_by_xpath('//XCUIElementTypeButton[@name="OK"]').click()
#返回绑定界面,等待绑定完成
sleep(10)
my_ios_driver.find_element_by_xpath('//XCUIElementTypeButton[@name="OK"]').click()
else:
#TODO 由于paypal的原因,支付完成后可能会出现pending状态而无法继续接着其他步骤,可能需要单独处理
device_id.clear()
device_id.send_keys(barcode)
self.hide_key_board()
bind_buttom.click()
sleep(5)
buy_now=my_ios_driver.find_element_by_xpath('//XCUIElementTypeButton[@name="Buy now"]')
buy_now.click()
sleep(5)
my_ios_driver.find_elements_by_class_name('XCUIElementTypeButton')[2].click()
sleep(5)
if self.utis.exists('xpath','//XCUIElementTypeButton[@name="Next"]',30):
my_ios_driver.find_element_by_class_name('XCUIElementTypeTextField').send_keys("accounting-buyer@latsen.com")
my_ios_driver.find_element_by_xpath('//XCUIElementTypeButton[@name="Next"]').click()
sleep(10)
my_ios_driver.find_element_by_class_name('XCUIElementTypeSecureTextField').send_keys("paypalDev123")
my_ios_driver.find_element_by_xpath('//XCUIElementTypeButton[@name="Log in"]').click()
else:
my_ios_driver.find_element_by_class_name('XCUIElementTypeTextField').send_keys("accounting-buyer@latsen.com")
my_ios_driver.find_element_by_class_name('XCUIElementTypeSecureTextField').send_keys("paypalDev123")
self.hide_key_board()
my_ios_driver.find_element_by_xpath('//XCUIElementTypeButton[@name="Log in"]').click()
sleep(15)
my_ios_driver.execute_script("mobile:swipe", {"direction": "up", 'element': my_ios_driver.find_element_by_xpath(' //XCUIElementTypeStaticText[@name="Pay with"]'), "duration": 1})
my_ios_driver.find_element_by_xpath('//XCUIElementTypeButton[@name="Agree and Subscribe"]').click()
else:
device_id.clear()
device_id.send_keys(barcode)
self.hide_key_board()
bind_buttom.click()
# 优惠码购买完成,但服务器设备状态未进行更改
#处理不同型号机器弹框不同问题
# sleep(10)
# my_ios_driver.set_page_load_timeout(10000)
sleep(15)
if type=="SUBSCRIBABLE":
my_ios_driver.find_element_by_xpath('//XCUIElementTypeButton[@name="OK"]').click()
#P
else:
my_ios_driver.find_element_by_xpath('//XCUIElementTypeButton[@name="Yes"]').click()
用到sleep简直就是噩梦,但不得不用,官方的等待方式经常会导致元素查找异常,哪怕我已经做了异常处理,场景多变,况且我还是菜;
# TODO 修改代码,返回一个元组,里面里面存放元素是否存在和控件对象
# TODO 修改代码,返回一个元组,里面里面存放元素是否存在和控件对象
def exists(self, what_method, resource_text, timeout=1):
i = 0
flag = False
driver = my_ios_driver
while timeout > i:
try:
if what_method == "accid":
driver.find_element_by_accessibility_id(resource_text)
elif what_method == "class":
driver.find_element_by_class_name(resource_text)
elif what_method == "css":
driver.find_element_by_class_name(resource_text)
elif what_method == "xpath":
driver.find_element_by_xpath(resource_text)
elif what_method == "pred":
driver.find_element_by_ios_predicate(resource_text)
elif what_method == "tag":
driver.find_element_by_tag_name(resource_text)
elif what_method == "name":
driver.find_element_by_name(resource_text)
elif what_method == "id":
driver.find_element_by_id(resource_text)
flag = True
return flag
except Exception as e:
print(e)
i += 1
continue
return flag
更多的代码细节不能太多暴露,请原谅
配合unittest使用,设计合理的测试流程,即可完成整个APP的全部用例的测试;
from ios.ios_app_edit_user_info_testcase import test_edit_user_profile
from ios.ios_app_register_testcase import Register_TestCase
from ios.ios_app_edit_pet_info_testcase import Edit_Pet_Info
if __name__ == '__main__':
testcase = unittest.TestLoader().loadTestsFromTestCase(Login_Test)
test_edit_user_profile = unittest.TestLoader().loadTestsFromTestCase(test_edit_user_profile)
FindPassword = unittest.TestLoader().loadTestsFromTestCase(FindPassword)
Register_TestCase = unittest.TestLoader().loadTestsFromTestCase(Register_TestCase)
add_pet=unittest.TestLoader().loadTestsFromTestCase(AddPet_TestCase)
edit_pet=unittest.TestLoader().loadTestsFromTestCase(Edit_Pet_Info)
suite = unittest.TestSuite([testcase,add_pet,edit_pet,test_edit_user_profile,FindPassword,Register_TestCase])
report='/Users/kefeng/Documents/TestCases/reports/testcase_report.html'
with(open(report, 'wb')) as fp:
runner = HTMLTestRunner(
stream=fp,
title='<project name> iOS 测试报告',
description='describe: iOS 测试报告'
)
runner.run(suite)
原先想着可以录屏给亲们看个效果,但是发现模拟器似乎没有录屏功能,如果下次拿到录屏,一定给补上🤣
一下截图是测试结束后的报告,全部59个testcase可以在17分钟完成测试,下次可以喝着肥宅水,看着自动化脚本运行即可,当然目前代码的健壮性需要加强,大家一起努力吧
目录
6:告一段落
祝愿和我一样的菜鸟能快速成长,开心每一天。