分層測試_有贊項目實踐

1. 背景
先理一下自動化測試的概念,從廣義上來講,一切經過工具(程序)的方式來代替或者輔助手工測試的行爲均可以成爲自動化。從狹義上來講,經過編寫腳本的方式,模擬手工測試的過程,從而替代人工對系統的功能進行驗證。
有贊是一家互聯網行業的創業公司,測試起步較晚,發佈很是頻繁,就算每次只回歸核心功能,對人數極少的幾個測試人員來講工做量巨大,且基本是重複勞動,極其枯燥,持續時間長了也容易出錯。
因此初期咱們測試自動化切入的思路很是簡單:從實際用戶的角度出發,模擬真實的操做,替代現有的手工測試用例的執行。這樣一來,每次重複的工做就能夠用自動化來替代,測試人員只須要關注每次發佈的增量需求便可。
隨着腳本數量的增長,這種自動化覆蓋的方式的弊端也逐漸暴露:
  • 執行效率低下
  • 構建成功率低(誤報率高)。
  • 受前端樣式變動影響大
  • 外部依賴較多,不是全部用例都能自動化
  • 覆蓋能力有限
雖然咱們在測試框架和工具層面經過結合selenium-grid實現了腳本併發執行和失敗用例重試機制以提升執行效率和下降誤報率,可是這種方式只能緩解問題,並不能從根本解決覆蓋不全的問題。
正好遇上公司的SOA服務化進程,測試這邊也開始配合的作自動化方面的轉變,從原來的黑盒系統級自動化測試向分層自動化測試轉變。
2. 分層自動化測試
在談分層測試以前,先回顧幾個概念:
  • 單元測試:對軟件中的最小可測試單元進行檢查和驗證。具體的說就是開發者編寫的一小段代碼,用於檢驗被測代碼的一個很小的、很明確的功能是否正確。一般而言,一個單元測試是用於判斷某個特定條件(或者場景)下某個特定函數的行爲。
  • 集成測試:集成測試是在單元測試的基礎上,測試在將全部的軟件單元按照概要設計規格說明的要求組裝成模塊、子系統或系統的過程當中各部分工做是否達到或實現相應技術指標及要求的活動。也就是說,在集成測試以前,單元測試應該已經完成。這一點很重要,由於若是不通過單元測試,那麼集成測試的效果將會受到很大影響,而且會大幅增長軟件單元代碼糾錯的代價。
  • 系統測試:將需測試的軟件,做爲整個基於計算機系統的一個元素,與計算機硬件、外設、某些支持軟件、數據和人員等其餘系統元素及環境結合在一塊兒測試。系統測試的目的在於經過與系統的需求定義做比較,發現軟件與系統定義不符合或與之矛盾的地方。
接下來咱們談談有贊是如何隨着系統拆分SOA服務化推動分層自動化測試的。先來看看經典的測試金字塔:

 

其中Unit表明單元測試,Service表明服務集成測試,UI表明頁面級的系統測試。分層的自動化測試倡導產品的不一樣層次都須要自動化測試,這個金字塔也正表示不一樣層次須要投入的精力和工做量。下面我會逐層介紹有讚的分層自動化實踐。
2.1 Unit-單元測試
在系統拆分以前,有贊只有一個龐大的巨無霸系統,單元測試極度缺失。在系統逐漸SOA服務化的過程當中,咱們逐漸提出了對單元測試覆蓋率的要求。
咱們的單元測試會分別作DAO層和服務層的測試。DAO層的單元測試主要保障SQL腳本的正確性,在作服務層的單元測試時就能夠以DAO層是正確的前提進行用例編寫了。
爲了作細粒度的測試,須要解決單元測試的外部依賴。系統和模塊之間的依賴能夠經過Mock框架(Mockito/EasyMock)解耦,同時能夠結合h2database解決對數據庫的依賴,使得測試用例儘量作到能夠隨時隨地運行。
這一層發現並解決問題付出的成本相對來講最低,自動化用例的維護成本也不高,總的來講自動化測試的投入產出比最高。
單元測試的責任主體通常來講是開發人員,寫單元測試也是開發人員對本身的代碼進行檢查的一個過程。
2.2 Service-服務集成測試
咱們在服務層的測試首要考慮的是各系統(子系統)的集成測試。由於在通過單元測試這一層的保障以後,在服務層咱們更關注的是某個系統的輸入輸出功能是否正確,以及若干個系統間的交互是否和業務場景的要求一致。
先來看看咱們系統拆分以後的SOA系統應用架構圖:

 

  • 展示層:老的Iron應用,代碼爲php。拆分以後iron只剩下和前端交互的展示層邏輯,以及調用核心業務的API層
  • 核心業務:Iron系統拆分出來的核心業務
這一層的被測對象是抽離了展示層的代碼(前端以及部分後端展示層邏輯)。
鑑於有讚的測試起步較晚(應該不少創業公司都有相似狀況),測試資源緊缺,代碼覆蓋率低得可憐。因此咱們的初期自動化用例覆蓋策略是這樣的:
  • 從老的Iron應用的API接口做爲業務場景覆蓋的切入點
  • 優先覆蓋核心業務的場景
  • 配合系統拆分,優先覆蓋拆分出去的系統
  • 已拆分出去的系統,作好系統服務層的測試覆蓋(全面覆蓋該服務的接口)
  • 測試依賴的數據準備優先選擇調用系統接口的方式(爲了增長業務覆蓋面)
  • 測試方式逐漸從黑盒向灰盒/白盒轉變
這樣作的好處是,能夠快速增長業務場景的覆蓋面,同時事先準備好的API接口用例,能夠做爲系統拆分後的冒煙測試用例,起到核心老功能的迴歸做用(只是作系統拆分,業務邏輯以及對展示層暴露的接口行爲不變)。畢竟在自動化測試的過程當中,最怕的就是變化,會帶來更多的腳本維護工做,而以這種方式覆蓋的用例,目前來看維護成本很低。
再介紹一下這一層的初期咱們用例的基本形態:
  • 專一於業務場景,和UI腳本一致,只是腳本從操做頁面變成了調用接口。相對於UI自動化,服務層的接口測試更加穩定,測試用例也更容易維護。服務層接口測試能夠更關注與系統總體的邏輯(業務)驗證,而UI自動化則會轉變爲頁面展現邏輯及界面前端與服務的集成驗證(這個在UI層會介紹)。
  • 暫時不作系統間的Mock。更多的考慮系統之間的耦合和依賴。
  • 搭建MockServer解決系統的外部依賴,主要是相似於支付等第三方系統(關於咱們的MockServer會有專門的文章介紹)。
結合咱們的交易系統舉個例子:好比交易系統會依賴於商品和營銷活動,那咱們的下單場景的用例會依次調用商品和營銷這幾個系統的API構造數據做爲用例的前置條件,而後按照下單的業務場景調用交易系統的下單接口,校驗返回值以及寫入DB的數據,最後作好數據清理的工做。
咱們在這一層的測試框架選擇是基於公司通用的服務框架(Nova)基礎之上搭建的,架構圖以下:

 

BIT :服務接口集成測試(Business Interface&Integration Test)
SUT:被測系統(System Under Test)
  • Validator:各個業務的數據庫結果驗證封裝
  • Mock:用例前置條件依賴的數據Mock服務
  • HttpClient:根據IRON系統的接口封裝了返回通用的RPCResult對象
  • Util:經常使用工具類封裝
  • Biz:在此封裝了全部被測系統對外暴露的接口,供測試用例直接調用
  • TestCase:咱們服務接口測試又分爲SDV(System design Verify-系統設計驗證)和SIT(System Integration Test-系統集成測試)。按照上面提到的用例覆蓋策略,咱們是在系統拆分以前,先根據該系統的業務場景和REST接口補充核心的接口集成測試用例,後續能夠做爲系統拆分以後的冒煙用例。在系統拆分以後,詳細補充該系統的測試用例,粒度更細。
  • Facade:結合了Nova框架,對外發布各個業務的數據構造的REST接口,同時能夠做爲測試數據構造系統,輔助測試人員的手工測試
這一層的測試覆蓋主要是由測試人員進行,是測試人員大展身手的地方。
咱們不須要很是詳細的瞭解代碼的實現,可是咱們的用例裏充分體現了咱們對系統的結構,模塊之間關係等的充分的理解。
後續咱們對於Service層自動化測試的推動策略是:
  • 逐漸豐富SDV層的測試用例,而且在必定程度上進行用例依賴的系統的解耦,好比數據構造從調用接口向直接往數據庫寫入數據轉變。
  • 逐漸細化拆分業務場景,作好用例的解耦。
  • 優先作場景覆蓋,以後再考慮代碼覆蓋。
2.3 UI-展示測試
先提一個問題,既然在文章開篇提到了UI自動化測試有這麼多弊端,這麼勞民傷財,那麼是否還有必要進行UI層的自動化呢?答案是確定的,由於UI層是咱們的產品最終呈現給用戶的東西。因此在作好上面兩層的測試覆蓋以後,測試人員能夠投入更多的精力到UI層的測試上。正是由於測試人員會在UI層投入較大精力,咱們仍是有必要經過自動化來幫助咱們解放部分重複勞動力。
根據咱們的UI層自動化實踐,提一下咱們的UI層自動化覆蓋的原則:
  • 能在底層作自動化覆蓋,就儘可能不在UI層作自動化覆蓋
  • 只作最核心的功能的自動化覆蓋,腳本可維護性儘量提升
咱們提升UI腳本可維護性的方法是遵循Page Object設計模式。
Page Object
Page Object模式是爲了不在測試代碼中直接操做HTML元素,對Web頁面的抽象。好處有:
  • 減小測試代碼的冗餘
  • 提升測試代碼的可讀性和穩定性
  • 提升測試代碼的可維護性
一個簡單的例子
以有贊首頁的登陸操做爲例(Ruby):
class LoginPage include HeaderNav def login(account, password) text_account.wait_until_present.set(account) text_password.set(password) button_login.wait_until_present.click return MainPage.new(@browser) end private def text_account @browser.text_field(:name => 'account') end def text_password @browser.text_field(:name => 'password') end def button_login @browser.button(:class => 'login-btn') end end
  • public方法對外暴露頁面的服務,對於登陸頁面來講就是登陸行爲
  • 頁面的UI細節設爲private方法對外隱藏
  • 跳轉到新的頁面後在此方法中return以後的頁面的對象,好比登陸以後跳轉到首頁(MainPage)。甚至同一頁面也能夠返回self作鏈式操做。
  • 各個頁面的公共部分,如頁面頂部導航,能夠封裝成Module供各個頁面對象直接include
下面咱們來看看測試用例:
class TestLogin < Test::Unit::TestCase def testLogin @browser = Browser.new @browser.goto 'youzan.com' main_page = @browser.login_page.login('xx', '123') #斷言 end end
這樣最終的測試腳本呈現的就是單純的頁面操做邏輯,更貼近文本測試用例。
下面來看一下咱們的測試框架:

 

  • Base:這一層和大多數UI測試框架大同小異,使用的是selenium和watir,用例管理方面並無使用Ruby領域煊赫一時的BDD框架cucumber,而是最基本的單元測試框架MiniTest。同時還引入了ruby的多線程包,配合UI腳本的併發執行。
  • Actir:咱們本身封裝的測試框架
    • Initializer:自動按照約定的工程結構加載全部的ruby文件,並根據Page的類名和反射自動生成了全部頁面類的對象實例。
    • UA:封裝好測試須要的瀏覽器User-Agent。
    • Executor:用例執行器。基於ruby的多線程包以及selenium-grid,實現了全部用例的調度及分佈式執行,能夠必定程度上大大提高UI腳本的執行效率。執行器還包括了失敗用例重試機制。
    • Util:工具類,包括了配置文件讀寫、數據驅動等。
    • Report:根據UI測試腳本執行的最終結果(失敗重試的用例以最終的結果爲準)自動生成HTML格式的測試報告。
    • Cli:根據Actir框架的上述功能,封裝出的命令行工具,方便持續集成。
  • Project
    • Pages:基於PageObject模式包裝出的頁面的對象
    • Components:各個頁面的公用的部分或者插件,如圖片上傳、地址選擇等。包裝成Module供各個頁面對象須要時直接include。
    • Item:根據系統業務抽象出的對象,如訂單、優惠券、商品等
    • User:根據系統業務抽象出的角色及其Action,如買家的購買行爲、買家的退款、發貨等。
隨着服務層自動化覆蓋率愈來愈高,UI層的自動化覆蓋會逐漸轉變爲頁面展現邏輯及界面前端與服務的展示層交互的集成驗證。咱們後續對於UI層自動化的演進規劃是這樣的:
  • 依賴環境的Mock,解除UI腳本的外部依賴
  • 完善的數據準備,能夠經過後端服務接口的mock使UI自動化更關注頁面業務邏輯的自動校驗。
  • 頁面截圖比對校驗UI樣式。
UI層的自動化測試也是由測試人員負責,在覆蓋了核心業務核心場景以後,不該該在這層的自動化覆蓋上投入太多的精力和資源。就算咱們在必定程度上提升了腳本的可維護性,但是畢竟自動化測試最怕的就是變化,而UI界面是變化頻率最高的一層,因此仍是得投入必定的精力維護,不是麼?
3. 持續集成
有了上述各層的自動化測試腳本,下面咱們須要創建起持續集成體系。 持續集成的目的:
  • 流程自動化,提升工做效率
  • 最大化自動化測試腳本的價值

 

咱們的持續集成是基於Jenkins搭建的,主要的動做以下:
  • 代碼提交自動執行單元測試
  • 單元測試經過後自動部署總體的環境
  • 自動執行集成自動化測試(Service/UI)
  • 自動生成構建的詳細測試報告,同時自動通知相關人員
持續集成所需的支撐有:
  • 測試環境自動部署腳本
  • 代碼覆蓋率自動收集
    • Java應用 基於JaCoCo+Jenkins插件的方式
    • php應用 通Xdebug+phpunit的方式
  • 測試報告相關插件及腳本
  • 代碼靜態檢查等
對於持續集成咱們後續的演進規劃是朝着持續交付和持續部署的方向努力,在持續集成的基礎之上,自動將代碼部署到測試環境上方便測試人員進行手工測試。
4. 總結
本文主要從總體上介紹了在有贊SOA化的進程中,測試推行的分層自動化實踐,以及後續的發展方向,同時簡單介紹了相關的測試框架結構。下面再從總體回顧一下咱們的分層自動化的要點:

 

  • 單元測試:
    • 優先級最高
    • 粒度最細、覆蓋面最全
    • 開發實施
  • 服務測試
    • 對測試來講優先級最高
    • 從業務場景的角度切入
    • 系統外部接口100%覆蓋
    • 關注系統間的依賴和調用
    • 測試實施
  • 頁面測試
    • 優先級相對最低
    • 只保證核心功能的自動化覆蓋,只關注UI層的問題
    • 經過數據mock的方式減小對後臺數據的依賴。
    • 測試實施
至於各層投入的具體比重,仍是須要根據項目的需求來實際規劃。在《google 測試之道》一書,對於google產品,70%的投入爲單元測試,20%爲集成、接口測試,10% 爲UI層的自動化測試。
最後再提一些觀點吧:
  • 越底層的自動化,收益越高
  • 質量不是測試人員一我的的事情
  • 自動化測試的目的不是爲了減小手工測試,而是爲了測試人員作更多更有意義的手工測試
相關文章
相關標籤/搜索