一、Appium介绍
Appium是一个开源的自动化测试工具,其支持iOS和安卓平台上的原生的,基于移动浏览器的,混合的应用。
Appium 理念
Appium是基于以下的四个理念设计来满足移动平台测试自动化的要求的:
1)您不应该因为需要自动化测试您的应用而不得不以任何形式去重新编译或者修改你的app
2)您不应该把自己固定在一门特定的语言和一个特定的框架上去实现和运行你的测试
3)当说到测试自动化APIs的时候,一个移动测试框架不应该做“重新发明轮子”的事情,
4)一个移动测试自动化框架应该是开源的,无论是在精神上,实际上,还是名义上!
使用Appium进行自动化测试有两个好处
Appium在不同平台中使用了标准的自动化APIs,所以在跨平台时,不需要重新编译或者修改自己的应用。
Appium支持Selenium WebDriver支持的所有语言,如java、Object-C、JavaScript、Php、Python、Ruby、C#、Clojure,或者Perl语言,更可以使用Selenium WebDriver的Api。Appium支持任何一种测试框架.Appium实现了真正的跨平台自动化测试。(本文主要介绍Python的用法)
Appium架构
Appium 是一个用Node.js编写的HTTP server,它创建、并管理多个 WebDriver sessions 来和不同平台交互,如 iOS ,Android等等.
Appium 开始一个测试后,就会在被测设备(手机)上启动一个 server ,监听来自 Appium server的指令. 每种平台像 iOS 和Android都有不同的运行、和交互方式。所以Appium会用某个桩程序“侵入”该平台,并接受指令,来完成测试用例的运行。
二、Appium环境搭建(Android)
首先需要准备
1) jdk(步骤不再啰嗦)
2) android SDK,下载地址:http://developer.android.com/sdk/index.html,下载sdk tools,可能需要FQ,提供一个国内下载地址:http://www.androiddevtools.cn/
3) appium,下载地址:http://appium.io/
4) nodejs,下载地址:https://nodejs.org/en/
5) python, 下载地址:https://www.python.org/, 下载3.X 的版本。
上述软件都准备好后,则进入搭建步骤。
安装、配置
将上述软件依次安装。
1) android sdk安装完毕后,需要配置环境变量
新建ANDROID_HOME D:\ProgramFiles (x86)\Android\android-sdk
在PATH中添加:%ANDROID_HOME%\platform-tools;%ANDROID_HOME%\tools;
2) nodejs安装完毕后,需要配置环境变量
在PATH中添加:D:\Program Files\nodejs;
3) appium安装完毕后,需要配置环境变量
D:\Program Files (x86)\Appium\node_modules\.bin;
4) 配置好后,启动cmd,
输入node -v,查看node安装版本
输入appium-doctor检查appium的安装环境是否成功,如下图:
5) 安装Python,配置环境变量,如C:\Python27,检查是否设置成功
三、开始例子(Python)
1) 启动Appium
打开命令行,输入appium, 显示成功启动:
2)连接Android手机(或者模拟器)
3)编写客户端代码
创建文件hello_appium.py , 编辑内容:
#coding=utf-8
from appium import webdriver
desired_caps = {}
desired_caps['platformName'] = 'Android'
desired_caps['platformVersion'] = '4.4.2'
desired_caps['deviceName'] = 'Android Emulator'
desired_caps['appPackage'] = 'com.android.calculator2'
desired_caps['appActivity'] = '.Calculator'
driver = webdriver.Remote('http://localhost:4723/wd/hub', desired_caps)
driver.find_element_by_name("1").click()
driver.find_element_by_name("5").click()
driver.find_element_by_name("9").click()
driver.find_element_by_name("9").click()
driver.find_element_by_name("5").click()
driver.find_element_by_name("+").click()
driver.find_element_by_name("6").click()
driver.find_element_by_name("=").click()
driver.quit()
4)运行
打开命令行,cd到E:\PythonTest\AppiumClientPython 中,运行 python hello_appium.py, 正常情况可以看到手机按照代码控制,打开计算器,逐个点击按钮完成计算。
四、Appium文档
安装应用后打开
import os
from appium import webdriver
APK_PATH = 'apk/ECloud-debug.apk'
COMMAND_EXECUTOR_URL = 'http://localhost:4723/wd/hub'
desired_caps = {}
desired_caps['platformName'] = 'Android'
desired_caps['platformVersion'] = '5.0'
desired_caps['deviceName'] = 'Android Emulator'
desired_caps['app'] = os.path.abspath(APK_PATH)
driver = webdriver.Remote(COMMAND_EXECUTOR_URL, desired_caps)
查找控件
1)通过名称查找
btn = driver.find_element_by_name("+")
2) 通过ID查找
start_btn =driver.find_element_by_id('com.cn21.ecloud:id/instruction_close_btn')
或 start_btn = driver.find_element_by_id('instruction_close_btn')
3)通过类名查找
child_text =parent.find_element_by_class_name('android.widget.TextView')
4)通过android_uiautomator查找
start_btn =driver.find_element_by_android_uiautomator('new UiSelector().clickable(true)')
以上find_element_by_XX 都是返回符合条件的第一个控件,如果要返回多个控件,可以调用 find_elements_by_XX, 返回的是一个list。
注意:如果找不到符合条件的控件,会抛出异常。
5)查找结点,不希望返回异常,写个函数就行了
def find_element_by_id_no_except(driver, id):
element = None
try :
element = driver.find_element_by_id(id)
except Exception,e:
print Exception, ':', e
return element
模拟按钮点击
login_btn.click()
注意:有的点击如果需要等待动画、或者网络请求,建议等待一会:
import time
time.sleep(2) # 睡眠2秒
输入框输入文本
user_input.send_keys('123456')
注意:Android如果要正确输入,需要把使用系统自带的输入法,第三方输入法无法正确输入。
模拟点击返回键
driver.press_keycode(4)
其中按钮的定义,由Android里的KeyEvent.java里定义的,所以其它的Android按钮也是支持的。
关闭driver
driver.quit()
注意:一定要记得关闭driver, 否则下次连接的时候可能会出异常,因为Appium以为你上次未关闭,会创建Session失败。
为了避免代码出现异常而没有关闭,可以在捕获异常时再关闭。
滑动界面
下面的例子,演示点击屏幕中间,并向上拉动(相当于查看列表下面的内容了)。
from appium.webdriver.common.touch_action import TouchAction
def test_scroll_down(driver):
screen = driver.get_window_size()
action = TouchAction(driver)
action.press(x=screen['width']/2,y=screen['height']/2)
action.move_to(x=0,y=-screen['height']/10)
action.release()
action.perform()
等等,怎么获取界面的属性来验证正确性?
获取界面属性,控件属性
1)获取当前Activity名称
activity = driver.current_activity
2) 获取屏幕宽高
screen = driver.get_window_size()
3)获取控件文本
mobile_name.get_attribute('text') 或者 mobile_name.text
4)获取控件类名
mobile_name.get_attribute('className')
5)判断控件是否显示
mobile_name.is_displayed() 或者 mobile_name.get_attribute('displayed')
6)获得控件位置
mobile_name.location
7)获得控件大小
mobile_name.size
8)查找控件子结点
parent.find_elements_by_class_name('android.widget.TextView')
同样:查找控件的其它方法,也适用于查找子结点。
对于交互后的验证,无法验证到具体的数据内容,可以验证当前的Activity,或者文本,或者列表是否为空等等。
更多参考:http://blog.csdn.net/crisschan/article/details/50416860
五、结合单元测试框架编写用例
Python自带有unittest用于单元测试,其结构类似于JUnit。
一个测试类需要继承于unittest.TestCase, 方法setUp 用于测试初始化,每个用例开始前都会调用,tearDown用于用例结束时调用,每个以test开始的函数被当成一个用例。
test_random.py
import random
import unittest
class TestSequenceFunctions(unittest.TestCase):
def setUp(self):
self.seq = range(10)
def test_shuffle(self):
# make sure the shuffled sequence does not lose any elements
random.shuffle(self.seq)
self.seq.sort()
self.assertEqual(self.seq, range(10))
# should raise an exception for an immutable sequence
self.assertRaises(TypeError, random.shuffle, (1,2,3))
def test_choice(self):
element = random.choice(self.seq)
self.assertTrue(element in self.seq)
def test_sample(self):
with self.assertRaises(ValueError):
random.sample(self.seq, 20)
for element in random.sample(self.seq, 5):
self.assertTrue(element not in self.seq)
if __name__ == '__main__':
unittest.main(verbosity=2)
运行此测试: python test_random.py 可以查看测试的结果
上面结果显示,有2个用例测试通过,1个用例不通过。
可以在一个目录下写多个以test开头的测试文件,然后通过以下命令运行所有测试类:
python -m unittest discover . -v
六、完整例子
测试登陆登出功能
test_ecloud_login_logout.py
#coding=utf-8
#
测试天翼云登陆登出功能
#
用例1:快速登陆,验证登陆后的Activity为MainPageActivity
# 用例2:普通登陆,输入用户名密码,验证登陆后的Activity为MainPageActivity
# 用例3:快速登陆后注销,验证注销后的Activity为LoginActivity
import unittest
import appium_ecloud
import appium_util
from appium import webdriver
import os
class LoginLogoutTest(unittest.TestCase):
##
def setUp(self):
#print('Installing ...')
desired_caps = {}
desired_caps['platformName'] = 'Android'
desired_caps['platformVersion'] = '5.0'
desired_caps['deviceName'] = 'Android Emulator'
desired_caps['app'] = os.path.abspath(appium_ecloud.APK_PATH)
self.driver = webdriver.Remote(appium_util.COMMAND_EXECUTOR_URL, desired_caps)
def tearDown(self):
self.driver.quit()
def test_FastLogin(self):
appium_ecloud.agree_document(self.driver)
appium_ecloud.quick_login(self.driver)
self.assertTrue(self.driver.current_activity.endswith('MainPageActivity'))
def test_SlowLigin(self):
appium_ecloud.agree_document(self.driver)
appium_ecloud.slow_login(self.driver)
self.assertTrue(self.driver.current_activity.endswith('MainPageActivity'))
def test_Logout(self):
appium_ecloud.agree_document(self.driver)
appium_ecloud.quick_login(self.driver)
if (self.driver.current_activity.endswith('MainPageActivity')):
appium_ecloud.test_logout(self.driver)
self.assertTrue(self.driver.current_activity.endswith('LoginActivity'))
if __name__ == '__main__':
unittest.main(verbosity=2)
自动乱点测试崩溃的情况
auto_test_ecloud.py
#coding=utf-8
import random
import time
import traceback
import appium_ecloud
def auto_interact(driver):
activity = driver.current_activity
# 一定的机率滑动,返回键,点击
rate = random.random()
if rate < 0.1:
print activity + ' Scroll Down'
appium_ecloud.test_scroll_down(driver)
elif rate < 0.2:
print activity + ' Scroll Up'
appium_ecloud.test_scroll_up(driver)
elif rate < 0.3:
print activity + ' Key Back'
driver.press_keycode(4)
else:
btn_list = driver.find_elements_by_android_uiautomator('new UiSelector().clickable(true)')
if (len(btn_list) > 0):
index = random.randint(0, len(btn_list) - 1)
print activity + ' Click Button index = %d' % (index,)
btn_list[index].click()
def main():
driver = None
try:
driver = appium_ecloud.install_app()
time.sleep(appium_ecloud.LONG_WAIT_TIME)
appium_ecloud.agree_document(driver)
appium_ecloud.quick_login(driver)
step = 0
while step < 100:
if (driver.current_activity.endswith('LoginActivity')):
appium_ecloud.test_login(driver)
elif (driver.current_activity.endswith('.Launcher')):
driver.background_app(1)
driver.launch_app()
else:
auto_interact(driver)
time.sleep(appium_ecloud.CLICK_WAIT_TIME)
step += 1
# 正常退出
driver.quit()
except Exception, e:
print Exception, ":", e
traceback.print_exc()
# 异常退出
if (driver != None):
driver.quit()
if __name__ == '__main__':
for i in range(20000):
try:
main()
except Exception, e:
print Exception, ":", e
traceback.print_exc()
注:为了更方便的编写python代码,推荐使用PyCharm IDE,编辑代码跟Java一样方便。
七、参考资料:
1)官网 http://appium.io/index.html
2)appium/python-client使用文档https://github.com/appium/python-client
3)搭建appium的android环境http://www.cnblogs.com/qiaoyeye/p/5131382.html
4)Appium移动自动化测试(四)http://www.cnblogs.com/fnng/p/4579152.html
5)AppiumPython API http://blog.csdn.net/crisschan/article/details/50416860
6)appium常用方法总结 http://www.cnblogs.com/fanxiaojuan/p/4882676.html