名词解释:
appium:是个web接口服务,接受http请求,可以开启多个端口,默认4723
appium desktop:内嵌了appium,还提供了属性配置、脚本录制等功能
Appium Client:appium为其提供服务端口,他就是请求的发送者
appium是什么?
appium是自动化测试工具,可以用来测试iOS和Android平台的应用、Web应用和Web混合应用。在测试中主要用于流程测试。
他有一个比较重要的特点,支持跨平台,下面的工作原理会提到为什么可以跨平台的
工作原理
appium选择了Client/Server的设计模式。
我们使用自己所熟知的语言编写脚本,通过相应语言环境的Client,发送http请求到Server端,Server端得到脚本,通过解析,驱动设备执行appium脚本。
1、Client端针对不同的语言开发了相应的appium库,那么我们写脚本就可以根据自己所熟悉的语言下载自己熟悉的appium库。我们所熟知的语言有:Ruby、Python、Java、JavaScript (Node.js)、Objective C、PHP、C# (.NET)、RobotFramework。后面附加相应语言的客户端。
2、Server端支持两大平台MAC和Windows。我们针对不同的平台安装使用。
3、移动设备支持iOS、Android两大真机和分别对应的模拟器。
总结:多语言、跨平台。appium的脚本支持多语言开发、同时可以在不同的平台运行。
搭建环境
作为小白,从搭建环境到用来做一些简单的测试,期间遇到各种奇葩问题,绕了很多弯路。在这稍作总结,希望像我一样的小白们都能有所收获。
目前我在Mac系统下,使用Python开发测试脚本,测试iOS项目(Android项目目前还有问题。。。)。
Mac系统搭建环境
Android项目环境
Node.js,下载地址:https://nodejs.org/en/download/,为什么要裝node.js呢?appium server 是用node.js写的,安装node.js可以直接用npm命令安装appium,或相关 driver、server。
android SDK、jdk
appium
大体安装过程是这样的,brew(mac 套件管理器,包含很多实用工具)->node->npm->appium
命令行运行:appium-doctor,看看缺什么就装上什么
iOS项目环境
Mac系统:10.12.6
xCode开发工具(命令行Xcode Command Line Tools)大多开发iOS的同学环境应该都是正常的
Appium-Server:目前最新的1.6.2版本
按照上面的工作原理图 “client->server->移动设备”分步讲解安装过程。
appium client
不太清楚明明支持多种语言,为啥Python和Java测试脚本成为主流。不过前人栽树后人乘凉,从网上看了下Python这些例子,最终还是选择了appium client,其中还有一个最重要的原因,使用appium desktop 可以生成python脚本,试想,作为小白,把生成的脚本拿来简单一改就可以使用,这应该算是入门最快的捷径了。
废话不多说,直接上python client地址:https://github.com/appium/python-client,下载完成进入到python-clinet目录执行python setup.py install就可以,操作如下
git clone git@github.com:appium/python-client.git
cd python-client
python setup.py install
扩展,附赠其他脚本语言的client下载地址,安装大同小异,自行百度
Ruby https://github.com/appium/ruby_lib
Python https://github.com/appium/python-client
Java https://github.com/appium/java-client
JavaScript (Node.js) https://github.com/admc/wd
Objective C https://github.com/appium/selenium-objective-c
PHP https://github.com/appium/php-client
C# (.NET) https://github.com/appium/appium-dotnet-driver
RobotFramework https://github.com/jollychang/robotframework-appiumlibrary
appium server
appium desktop下载地址
https://github.com/appium/appium-desktop/releases/tag/v1.6.2
下面是Appium Server,地址和端口号都是默认的
点击启动Start Server就会开启监听,可以看到4723在监听中,之后运行脚本的所有日志都会在这里展示出来。
点击放大镜(Start Inspector Session),设置相应的Capabilities,为接入设备做准备。下面是我根据自己设备和公司项目的配置的Capabilities
关于Capabilities的配置有很多,你想要的都在这里
http://appium.io/docs/en/writing-running-appium/caps/
iOS程序是如何通过server传递指令控制界面的???
WebDriverAgent,是实现自动化测试工作的实施者(实施者,我自己起的名)。他可以在iOS平台为服务器实现控制远端设备。比如启动、杀死程序和界面点击、拖拽等。通过连接XCTest.framework和调用苹果的API直接在设备上执行命令。
下载地址:https://github.com/facebook/WebDriverAgent
下面是我录制的脚本
#-*- coding: UTF-8 -*-
# This sample code uses the Appium python client
# pip install Appium-Python-Client
# Then you can paste this into a file and simply run with Python
import time
from appium import webdriver
caps = {}
caps["platformName"] = "iOS"
caps["deviceName"] = "iPhone"
caps["udid"] = "7e0d8f2dcfbcf64612ae93b087d51bff1f431"
caps["noReset"] = "true"
caps["bundleId"] = "com.yz.spacebridge"
caps["platformVersion"] = "11.2"
caps["xcodeOrgId"] = "<2MUJFSGB46>"
caps["xcodeSigningId"] = "iPhone Developer"
driver = webdriver.Remote("http://localhost:4723/wd/hub", caps)
el1=driver.find_element_by_xpath("//XCUIElementTypeButton[@name=\"登录\"]")
el1.click()
time.sleep( 5 )
els4 = driver.find_elements_by_name("千喜鹤美食广场")
els4[0].click()
time.sleep( 5 )
els5 = driver.find_elements_by_name("4dc370c7")
els5[0].click()
el2 = driver.find_element_by_accessibility_id("back")
el2.click()
el3 = driver.find_element_by_accessibility_id("back")
el3.click()
driver.quit()
值得注意的是,我们经常会遇到编码问题,如上面添加注释,注意,此注释一定要放在最顶端否则不起作用
#-*- coding: UTF-8 -*-
appium desktop,基本使用
要操作或者查看某个(某组)元素,必须事先知道该元素的id或者xpath,通过appium desktop可以很方便的定位获取页面元素id 和xpath。另外还有一些基础的页面操作,如下图:
纵横客云小盒收银接入
好长一段时间的调研和学习,最终都要落实到工作和项目中,在云小盒收银,我们会有所体现。
简单讲下收银模块如下:设置、交易统计、收款、订单
使用appium进行测试,说白了,就是测试整个流程通不通,然后按照“登录->交易统计->收银->核对统计结果->退款”这个主流程一直循环跑下去。
据上模块所讲,按照测试流程按照模块在测试工程中依次划分下面这四个模块loginModel(从设置进入,主测登录)、cashierModel(收银)、refundModel(订单统计、退款)、statisticsModel(交易统计)。
主模块:jinyuanbao,统筹管理
threads = []
t1 = threading.Thread(target=runloop)
threads.append(t1)
if __name__ == '__main__':
for t in threads:
t.start()
总业务流程
def task1():
driver = webdriver.Remote("http://localhost:4723/wd/hub", API.desired_caps)
time.sleep(2)
RefundClass().refundAction(driver)
# # 切换账号
account = LoginClass().loginAction(driver)
API.user_account = account
# 查看账单
total_dic = StatisticsClass().statisticsAction(driver)
if account != API.account2:
# 微信支付
dic = CashierClass().cashier_weixin_Action(driver)
# # 对比交易账单
if dic != None:
print dic
if dic != None and total_dic != None:
total_dic = StatisticsClass().payment_data_contrast(driver, total_dic, dic)
else:
print total_dic
return
# 退款
RefundClass().refundAction(driver)
# 支付宝支付
dic = CashierClass().cashier_alipay_Action(driver)
# 对比交易账单
if dic != None:
print dic
if dic != None and total_dic != None:
total_dic = StatisticsClass().payment_data_contrast(driver, total_dic, dic)
else:
print total_dic
return
# 退款
RefundClass().refundAction(driver)
通用模块 basemethod
主要实现如下功能:
1、本地记录日志
def report_log(self,content):
date_current = time.strftime("%Y-%m-%d", time.localtime())
time_current = time.strftime("%Y-%m-%d %H:%M:%S", time.localtime())
log_name = 'log/'+date_current+'.txt'
logfile = open(log_name, 'a+')
logfile.write('\n')
logfile.write('时间:'+time_current+'===')
logfile.write(content)
logfile.close()
2、通过id查找元素
def get_id(self,id,driver):
try:
element = WebDriverWait(driver,2).until(lambda driver: driver.find_element_by_id(id))
return element
except:
print '未定位到元素:'+id
return None
3、通过xpath查找元素
def get_xpath(self,xpath,driver):
try:
element = WebDriverWait(driver,2).until(lambda driver: driver.find_element_by_xpath(xpath))
return element
except:
print '未定位到元素:'+xpath
return None
4、钉钉webhook服务上报
def dingtalk(self,text):
self.report_log(text)
webhook = kwebhook
xiaoding = DingtalkChatbot(webhook)
content = text
xiaoding.send_text(msg=content,is_at_all=True)
5、钉钉webhook error上报
def ding_talk_error(self,text):
self.report_log(text)
webhook = kwebhook
xiaoding = DingtalkChatbot(webhook)
content = '收银账号:' + API.user_account + '\n' + text
xiaoding.send_text(msg=content, is_at_all=True)