一、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