目錄
1.1 常見的app型別:
1.2 為什么選擇Appium
1.3 Appium優點
1.4 Appium的設計
(二)appium client及server引數
2.1 Appium client
2.2 Appium server
2.2.1 原理
2.2.2 Server -args 啟動引數
(三) Find_element元素定位
3.1 element的定位方法:
3.2 Selenium 定位方式:
3.3 Appium 定位方式:
3.4 by_id('id')定位:
3.5 by_xpath('xpath')定位方式:
3.6 by_class_name定位:
3.7 By_android_uiautomator定位:
(1)UiSelector
(2)UIScrollable
3.8 By_accessibility_id定位
(四)Appium自動化測驗框架介紹與搭建
4.1 框架的幾大要素
4.2 框架的分層思想
4.3 如何持續集成
4.4 環境搭建
4.5 Nose框架介紹
4.6 框架資料的管理:
(五)框架中的AppiumServer模塊設計與撰寫
(六)框架內自動化測驗用例設計和方法封裝
(七)Jenkins持續集成
7.1 需要安裝的插件
7.2 SMTP郵件服務配置
7.3 app自動化測驗工程
(一)Appium簡介
1.1 常見的app型別:
-
native app--- 純原生app
-
Hybrid app--- 原生app中內嵌了webview
-
Web app --- html5的應用
1.2 為什么選擇Appium
借網上一張圖:

1.3 Appium優點
-
開源
-
跨架構:NativeApp、Hybird App、Web App
-
跨設備:Android、iOS、Firefox OS
-
不依賴原始碼
-
使用任何WebDriver兼容的語言來撰寫測驗用例,比如Java,Objective-C,JavaScript with Node.js,PHP,Python,Ruby,C#,Clojure或者Perl.
-
不需要重新編譯APP
1.4 Appium的設計
Appium的真正的作業引擎是第三方自動化框架,支持:
-
蘋果的UI Automator框架,支持ios平臺app自動化;
-
google’s uiautomator 只支持android4.2以上,僅支持NativeApp原生控制元件;
-
Selendrid基于Instrumentation框架,可測驗android2.3以上的系統,支持Hybrid、NativeApp;
-
Chromedriver 支持webapp、hybrid App;
Appium把這些第三方框架封裝成一套API,即WebDriver API
Appium擴充了WebDriver的協議,在原有的基礎上添加自動化相關的API方法,
(二)appium client及server引數
2.1 Appium client
Appium架構圖,Client即webdriver script部分(腳本部分)

Appium運行時候Server端會監聽 Client端發送的命令,接著在移動設備上執行這些命令,然后將執行結果放在HTTP回應中返還給客戶端,
2.2 Appium server
2.2.1 原理
Appium-Server端是基于node.js開發的程式,是http服務器,專門接收從client發送過來的命令,然后把命令發送到bootstrap.jar的客戶端(bootstrap是server端的一部分),bootstrap按照命令驅動UIautomator來實作命令的操作,完成后把結果回傳到server,server再通知appium-client,
啟動appium server方式:
(1)通過原始碼的方式啟動,即:到原始碼的目錄下,執行Node
(2)在命令列輸入appium來啟動
(3)使用appium.exe啟動
個人推薦使用appium命令啟動,便于進行CI集成(jenkins)
2.2.2 Server -args 啟動引數
Server啟動時的設定:
-
啟動server時指定設備
-
啟動server時指定埠號
-
設定session啟動的一些操作等等
appium啟動命令:
appium -p port --full-reset
常用的args:
(1)指定設備
-U,-udid: 指定連接的物理設備的UDID (通過adb devices 查看連接了那些設備)
(2)指定apk的路徑
--app: 指定apk檔案的絕對路徑
(3)指定日志的輸出
-g,--log: 將日志輸出到指定的檔案
(4)session間狀態管理
--full-reset: session結束之后會卸載應用
--no-reset: session之間不會操作應用
(5)指定監聽的ip和埠
-a,--address: 指定監聽的埠
-p,--port: 指定server的埠,默認是4723
-bp,--bootstrap-port: 指定連接設備的埠號,默認是4724
--selendroid-port: 指定與selendroid互動的埠,默認是8080
--chromedriver: 指定chromedriver運行的埠,默認是9515
(三) Find_element元素定位
3.1 element的定位方法:
(1)find_element_by_""
通過定位元素的屬性得到一個元素物件,若無回傳則拋例外
(2)find_elements_by_""(多個元素)
通過定位元素的屬性得到一個包含一個或多個元素串列(陣列),無回傳則拋例外
3.2 Selenium 定位方式:
(1)find_element_by_id('id') 'id'是唯一的
(2)Find_element_by_xpath(‘xpath’)‘xpath’可以是屬性,也可以使標準的路徑
(3)Find_element_by_link_text(‘login’)‘login’是a標簽里面的文字 <a>login</a>
(4)Find_element_by_name('name')<input name='username'>input標簽里會用到
(5)Find_element_by_class_name('className') 用法類似id,不一定唯一
(6)Find_element_by_css_selector('css’)webUI特有的定位方式
3.3 Appium 定位方式:
(1)Find_element_by_android('uiautomatorcode')使用UIautomator的代碼直接進行定位,UIautomator code是指所有的UIautomator定位方式(比如new UiSelector或者new UiScrollable做定位)
(2)Find_element_by_accessibility_id('text'or'id'or'content-desc')
(3)Find_element_by_ios('uiautomationcode')用法同等(1)
3.4 by_id('id')定位:
(1)resourceId
@appium1.4.0以前的版本
clock=driver.find_element_by_id('com.android.deskclock:id/analog_appwidget')
@Appium1.4.0或以后的版本
clock=driver.find_element_by_id('analog_appwidget')
Resourceid是相對唯一的,比如定義list(listid)串列下有多個item(itemid),此時id就會有重復,需要用find_elements
(2)Content-desc
例如:clock=driver.find_element_by_id('02:51')
優先匹配resourceId,然后匹配content-desc
3.5 by_xpath('xpath')定位方式:
(1)完整的路徑
NewsText=driver.find_element_by_xpath("//FrameLayout[1]/LinearLayout[1]/.......")
(2)通過元素屬性指定
//uiautomator模式
NewsText=driver.find_element_by_xpath("//android.widget.TextView[contains(@text,"新聞")]")
//selendroid模式
Driver.find_element_by_xpath("TextView[contains(@value,"新聞")]")
NewsText.click()
3.6 by_class_name定位:
className即android中的class,適用于控制元件比較少的情況下
Find_element_by_class_name('android.widget.TextView')[1] //獲取當前頁面所有的textview,然后按順序獲取
3.7 By_android_uiautomator定位:
(1)UiSelector
Find_element_by_android_uiautomator(‘newUiSelector().resourceId(“com.sankuai.meituan:id/city_button”)’)
(2)UIScrollable
Find_element_by_android_uiautomator(‘new UiScrollable(newUiSelector().scrollable(true)).scrollintoView(new
UiSelector().text(“新聞”))’)
3.8 By_accessibility_id定位
(1)resourceId
(2)Content-desc
(3)Text
Find_element_by_accessibility_id(u'美食')
優先匹配content-desc,然后匹配resourceId,最后匹配text

(四)Appium自動化測驗框架介紹與搭建
4.1 框架的幾大要素
自動化通用要素(web/介面/app):
-
資料/環境管理 ---資料如何維護(測驗環境/線上環境賬號、資料的不同、維護),環境切換(測驗環境/線上環境/預發布環境)
-
Log日志 ---appium server的日志,腳本代碼print的日志、框架運行的日志、手機運行時的日志
-
通用方法(util)---在資料、環境的處理上,對資料庫的操作、檔案的讀寫等較常用的方法
-
框架運行機制 ---框架是按照什么樣的順序運行,appium server何時啟動、何時得到設備、用例testcase套件怎么跑等
-
Result報表 ---測驗用例運行完畢后,查看運行的資訊、錯誤的失敗的情況進行報告決議
android自動化特有的驅動要素:
-
Appium server管理 ---如果有很多的設備、很多的用例,通過管理不同的appium server埠、appium server對應的設備號、appium server不同的啟動狀態
-
Driver管理 ---即session,每一個session可能對應不同的desired capibilities,對應不同的場景,是否每個用例腳本中要見session都需要考慮,對測驗效率有影響
-
元素物件管理 ---比如pageobject設計模式
-
Android設備管理 ---結合appiumserver管理使用,關注設備是否空閑,比如把100個用例分發到不同設備跑,提高效率,對android兼容性測驗也有幫助
4.2 框架的分層思想
開發者角度:
測驗套件(測驗框架junit/testNG/unitest)---本次使用nose的測驗框架(基于unitest),解決資料運行的管理、解決框架的運行機制、result報表(java推薦testNG,python推薦用nose)
Server ---包括設備和appium管理,才能對并發測驗進行很好分配(需要了解多執行緒和執行緒鎖等)
腳本撰寫角度:
腳本(testcase)---只寫testcase,不要寫資料
資料(data)
元素(Element)
三個層面不能摻雜,腳本呼叫資料,使用時,只需要把資料填充到一個地方,腳本呼叫就ok,資料變動不會影響到腳本
4.3 如何持續集成
什么是持續集成
CI平臺(Continuous Integration)
包含要素:
統一的代碼平臺
自動觸發構建、完成測驗得出報告
提交代碼會觸發構建
持續集成應該是一個完整的方案

實戰內容:
-
Python版本:
基于python+Nose+Nose-testconfig(管理資料配置)+Logging(日志系統)+Appium +jenkins+SVN(Git)
-
Java版本:
Java+TestNG+ReportNG+Maven+Appium+SVN(Git)
4.4 環境搭建
-
Java環境:jdk1.8.0_151
-
Android開發環境:adt-bundle-windows-x86_64-20140702
-
Python環境:python2.7.9
-
Appium環境:nodejs v5.6.0,appium1.3.4
-
腳本開發IDE:pycharm
-
其他組件:Nose&&Nose-testconfig,selenium,Appium-python-client,nosehtmloutput-2,nose-html-reporting插件等
環境搭建網上都有很多資料,此處不再贅述,
4.5 Nose框架介紹
(1) 繼承自unitest,比unitest更加簡單,功能更強大
(2) Nose提供了遞回查找測驗套件的功能,而unitest是代碼中通過呼叫unitest庫的testrun的方法去執行當前模塊下的測驗用例(代碼層面控制不方便),nose中只需要通過nose命令就可以遞回的尋找python檔案,通過正則匹配的方式發現test開頭或者包含test的檔案、方法,執行測驗,
Nosetest支持較多方便實用的插件,比如html報告的生成等集成插件
(3) 支持setup、teardown函式
有四種作用域:
-
package層
-
Module層
-
Class層
-
Function層
(4)可生成xml報告或html報告
例子如下:

_init_.py檔案
def setUp():
print 'package setUp'
def tearDown():
print 'package tearDown'
TestMyCase.py檔案
4.6 框架資料的管理:
全域資料(影響到框架運行流程的資料)-----通過testconfig檔案來實作支持
(1)存放通用性的全域資料,常見的如域名地址,介面測驗時的測驗/線上環境等域名是不同的;
(2)對于appium來說,可存放埠號(比如appium啟動埠4723,如果并發測驗,啟動多個server需要管理多個埠號)和一些全域控制引數(封裝好的點擊的方法,錯誤時截圖),
區域資料(不會影響到框架運行的全域資料) ---通過html或excel表格來獲取
比如發文章、評論的時用的資料
私有資料 ---寫到代碼模塊特定用例會用到的資料
Nose-testconfig的使用(全域資料的一個插件)
①在.cfg檔案中宣告testconfig檔案目錄;
[nosetests]
;--with-unit output xml report
with-html-output=True
html-out-file=result1.html
tests=testcase/testLogin/testLogin.py
nocapture=True
verbose=True
tc-format=python
tc-file=conf/env/testconfig.py
;exclude=testcase/testMyCase.py
②定義config字典,字典中可以填入全域變數
global config
config = {}
config['packageName'] = 'com.hwd.test'
config['all'] = 'acj'
config['appiumPort'] = 4723
config['selendroidPort'] = 8120
config['bootstrapPort'] = 4750
config['chromiumPort'] = 9553
config['app'] = r'D:\PyCharmWorkSpace\DemoProject\src\testData\xxxx-release-v4.2.0-2018-03-07.apk'
config['appPackage'] = 'com.cashregisters.cn'
config['appActivity'] ='com.hkrt.qpos.presentation.screen.welcome.WelcomeActivity'
config['appWaitActivity'] = ''
③在自動化腳本中匯入模塊,并使用變數
#coding:utf-8
from appium import webdriver
from testconfig import config
import logging,time
class testLogin:
def __init__(self):
self.logger =logging.getLogger(__name__)
def setUp(self):
desired_caps = {}
desired_caps['platformName'] = 'android'
desired_caps['platformVersion'] = '6.0.0'
desired_caps['deviceName'] = 'TWGDU1700002279'
desired_caps['app'] = config['app']
desired_caps['appPackage'] = config['appPackage']
desired_caps['appActivity'] = config['appActivity']
desired_caps['noReset'] = 'true'
desired_caps['unicodeKeyboard'] = 'true'
desired_caps['resetKeyboard'] = 'true'
if config['appWaitActivity'] != None:
desired_caps['appWaitActivity'] = config['appWaitActivity']
self.logger.info('Session Starting...')
self.driver =webdriver.Remote('http://localhost:4723/wd/hub',desired_caps)
def tearDown(self):
self.logger.info('Quit!')
self.driver.quit()
def testLogin(self):
self.logger.info('test logining!!!')
self.driver.implicitly_wait(10)
userFiled =self.driver.find_element_by_id('com.cashregisters.cn:id/phone_id')
pwdFiled =self.driver.find_element_by_id('com.cashregisters.cn:id/password_id')
userFiled.send_keys('18610000920')
pwdFiled.send_keys('1234qwer')
self.driver.find_element_by_id('com.cashregisters.cn:id/phone_id').click()
self.driver.find_element_by_id('com.cashregisters.cn:id/loginbutton').click()
time.sleep(2)


(五)框架中的AppiumServer模塊設計與撰寫
Appium Server的啟動方式
(1)使用appium.exe(windows)/appium.app(mac)
(2)通過代碼控制命令列啟動,通過下面例子可以看明白
例子:
①appium.py檔案撰寫appium的啟動模塊
import logging,os,time
import subprocess
from testconfig import config
class appium(object):
def __init__(self):
self.logger = logging.getLogger(__name__)
def start(self):
self.logger.info('Starting Appium Server ...')
currentTime = time.strftime('%Y%m%d%H%M%S',time.localtime())
udid = self.getUdid()
appiumPort = config['appiumPort']
bootStrapPort = config['bootstrapPort']
selendroidPort = config['selendroidPort']
chromiumPort = config['chromePort']
logPath = os.path.abspath(os.path.join(os.getcwd(),'log','AS'+currentTime+'.log'))
try:
self.process = subprocess.Popen('appium --port={} --bootstrap-port={} --selendroid-port={} --chromedriver-port={}'
'--log={} -U{}'.format(appiumPort,bootStrapPort,selendroidPort,chromiumPort,logPath,udid),
stdout=subprocess.PIPE,
shell=True)
except Exception,e:
self.logger.error('start appium server failed!')
self.logger.error('errorMsg:{}'.format(e))
time.sleep(5)
def stop(self):
self.logger.info('stop appium server.')
self.process.kill()
os.system('taskkill /im node.exe /f')
def getUdid(self):
'adb devices'
cmd = 'adb devices'
output = subprocess.Popen(cmd,stdout=subprocess.PIPE,shell=True)
infoList = output.stdout.read().strip('List of devices attached').split()
devicesList = []
if infoList != 0:
for deviceinfo in infoList:
if deviceinfo != 'devices':
devicesList.append(deviceinfo)
return devicesList[0]
②testconfig.py檔案設定啟動埠
global config
config = {}
config['packageName'] = 'com.hwd.test'
config['all'] = 'acj'
config['port'] = 4723
config['selendroidPort'] = 8120
config['bootstrapPort'] = 4750
config['chromePort'] = 9553
③用例中初始化啟動檔案__init__.py
#coding:utf-8
import logging
from src.AppiumServer import appiumserver
logger = logging.getLogger(__name__)
def setUp(self):
logger.info(u'啟動Appium Server')
appiumserver().start()
def tearDown(self):
logger.info(u'關閉Appium Server')
appiumserver().stop()
④撰寫測驗用例
#coding:utf-8
from appium import webdriver
from testconfig import config
import logging,time
class testLogin:
def __init__(self):
self.logger =logging.getLogger(__name__)
def setUp(self):
desired_caps = {}
desired_caps['platformName'] = 'android'
desired_caps['platformVersion'] = '6.0.0'
desired_caps['deviceName'] = 'TWGDU17000002279'
desired_caps['app'] = config['app']
desired_caps['appPackage'] = config['appPackage']
desired_caps['appActivity'] = config['appActivity']
desired_caps['noReset'] = 'true'
desired_caps['unicodeKeyboard'] = 'true'
desired_caps['resetKeyboard'] = 'true'
if config['appWaitActivity'] != None:
desired_caps['appWaitActivity'] = config['appWaitActivity']
self.logger.info('Session Starting...')
self.driver =webdriver.Remote('http://localhost:4723/wd/hub',desired_caps)
def tearDown(self):
self.logger.info('Quit!')
self.driver.quit()
def testLogin(self):
self.logger.info('test logining!!!')
self.driver.implicitly_wait(10)
userFiled =self.driver.find_element_by_id('com.cashregisters.cn:id/phone_id')
pwdFiled =self.driver.find_element_by_id('com.cashregisters.cn:id/password_id')
userFiled.send_keys('18610000920')
pwdFiled.send_keys('1234qwer')
self.driver.find_element_by_id('com.cashregisters.cn:id/phone_id').click()
self.driver.find_element_by_id('com.cashregisters.cn:id/loginbutton').click()
time.sleep(2)
執行順序:
框架的入口src/__init__.py (初始化日志相關的東西)—>testcase/__init__.py檔案(package層的init)—>testLogin/__init__.py啟動appiumserver—>testLogin方法
(六)框架內自動化測驗用例設計和方法封裝
(1)創建appiumserver,上面已介紹
(2)在run_test.cfg中指定只運行自動化測驗腳本testLoginSuccess.py檔案
[nosetests]
;--with-unit output xml report
with-xunit=True
tests=testcase/testLogin/testLoginSuccess.py
nocapture=True
verbose=True
tc-format=python
tc-file=conf/env/testconfig.py
;exclude=testcase/testMyCase.py
(3)新建BaseTestCase基類(測驗用例中繼承BaseTestCase即可使用)
#coding:utf-8
from appium import webdriver
from testconfig import config
import logging
class BaseTestCase:
def __init__(self):
self.logger = logging.getLogger(__name__)
def setUp(self):
desired_caps = {}
desired_caps['platformName'] = 'android'
desired_caps['platformVersion'] = '6.0.0'
desired_caps['deviceName'] = 'Google Nexus 5'
desired_caps['app'] = config['app']
desired_caps['appPackage'] = config['appPackage']
desired_caps['appActivity'] = config['appActivity']
desired_caps['noReset'] = 'true'
desired_caps['unicodeKeyboard'] = 'true'
desired_caps['resetKeyboard'] = 'true'
if config['appWaitActivity'] != None:
desired_caps['appWaitActivity'] = config['appWaitActivity']
self.logger.info('Session Starting...')
self.driver = webdriver.Remote('http://localhost:4723/wd/hub', desired_caps)
def tearDown(self):
self.logger.info('Quit!')
self.driver.quit()
(4)新建不同otherConfig檔案,指定不同的埠、實作不同apk應用運行不同的用例,上面已介紹
global config
config = {}
config['packageName'] = 'com.hwd.test'
config['all'] = 'acj'
config['appiumPort'] = 4724
config['selendroidPort'] = 8124
config['bootstrapPort'] = 4754
config['chromiumPort'] = 9554
config['app'] = r'D:\PyCharmWorkSpace\DemoProject\src\testData\xxx-release-v4.2.0-2018-03-07.apk'
config['appPackage'] = 'com.cashregisters.cn'
config['appActivity'] = 'com.hkrt.qpos.presentation.screen.welcome.WelcomeActivity'
config['appWaitActivity'] = None
(5)測驗腳本中匯入使用
import logging,time
from src.util.commonBase import *
from src.testcase.baseTestCase import BaseTestCase
class testLoginSucess(BaseTestCase):
def __init__(self):
BaseTestCase.__init__(self)
self.logger = logging.getLogger(__name__)
def setUp(self):
BaseTestCase.setUp(self)
def tearDown(self):
BaseTestCase.tearDown(self)
def testLoginFailed(self):
self.logger.info('test begaining!')
self.driver.implicitly_wait(10)
inputById(self.driver,'com.cashregisters.cn:id/phone_id','111111')
inputById(self.driver, 'com.cashregisters.cn:id/password_id', 'aaaaaa')
clickElementById(self.driver,'com.cashregisters.cn:id/loginbutton')
time.sleep(2)
def testLoginSuccess(self):
self.logger.info('test case 2.')
self.driver.implicitly_wait(10)
userFiled = self.driver.find_element_by_id('com.cashregisters.cn:id/phone_id')
pwdFiled = self.driver.find_element_by_id('com.cashregisters.cn:id/password_id')
userFiled.send_keys(18610000920)
pwdFiled.send_keys(1234qwer)
self.driver.find_element_by_id('com.cashregisters.cn:id/loginbutton').click()
time.sleep(2)
self.driver.find_element_by_id('android:id/button2').click()
(6)封裝方法 ---比如封裝登錄按鈕、截圖等等,使用例看起來更簡潔
import logging
logger = logging.getLogger(__name__)
def clickElementById(driver,elementId):
'''
:click element by id
'''
logging.info('click element by Id:{}'.format(elementId))
try:
driver.find_element_by_id(elementId).click()
except Exception,e:
logger.error('Click Id:{} Fail,error Msg:{}'.format(elementId,e))
def inputById(driver,elementId,text):
'''
:input sth into element by id
'''
logger.info('input {} into element by id:{}'.format(text,elementId))
try:
driver.find_element_by_id(elementId).send_keys(text)
except Exception,e:
logger.error('input fail,error msg:{}'.format(e))
(7)測驗用例中可以這樣使用:
inputById(self.driver,'com.cashregisters.cn:id/phone_id','111111')
inputById(self.driver, 'com.cashregisters.cn:id/password_id', 'aaaaaa')
clickElementById(self.driver,'com.cashregisters.cn:id/loginbutton')
(8)測驗報告

(七)Jenkins持續集成
Jenkins配置:網上教程很多,就不贅述,挑一些專案相關的實體
7.1 需要安裝的插件
此處用到2個插件:
(1)郵件插件email extension

(2)報告插件html-reporting和groovy

7.2 SMTP郵件服務配置
(1)管理員郵件配置:

(2)SMTP服務器配置:

(3)郵件通知配置:

7.3 app自動化測驗工程
(1)配置執行自動化測驗的腳本:

(2)測驗報告設定:

(3)收件人配置:

(4)測驗報告模板:

(5)配置郵件什么情況下發送(成功發送/失敗發送):

(6)構建完后jenkins頁面顯示:

(7)最終郵件收到的報告:

好了 學習也就到此結束了 想了解更多相關知識請關注我吧!下面是小編想對讀者大大們寫的一封信哦! 記住要認真讀哦!

感謝每一個認真閱讀我文章的人,看著粉絲一路的上漲和關注,禮尚往來總是要有的,雖然不是什么很值錢的東西,如果你用得到的話可以直接免費拿走:
① 2000多本軟體測驗電子書(主流和經典的書籍應該都有了)
② 軟體測驗/自動化測驗標準庫資料(最全中文版)
③ 專案原始碼(四五十個有趣且經典的練手專案及原始碼)
④ Python編程語言、API介面自動化測驗、web自動化測驗、App自動化測驗(適合小白


⑤ Python學習路線圖(告別不入流的學習)
上圖的資料 在我的QQ技術交流群里(技術交流和資源共享,廣告進來腿給你打斷)
可以自助拿走,群號768747503備注(csdn999)群里的免費資料都是筆者十多年測驗生涯的精華,還有同行大神一起交流技術哦
————————————————
「學習資料 筆記 工具 檔案領取」

掃描二維碼,
備注“csdn999”
小姐姐邀你一起學習哦~~
和志同道合的測驗小伙伴一起討論測驗技術吧!

一定一定一定 要備注暗號:CSDN999
———————————————

轉載請註明出處,本文鏈接:https://www.uj5u.com/qita/396113.html
標籤:其他
