分享第二章,關於測試驅動。這裏的測試主要針對Web後端的測試 —— 你爲何要寫測試用例(即測試用例的完善是不是浪費時間),如何完善你的測試用例,代碼設計如何簡化測試用例的書寫,以及一些後期的構想。javascript
這個習慣一般會被認爲是一種耽誤開發進度的行爲,你須要花費幾乎和開發代碼相同的時間來逐步完善你的測試用例。可是在開發過程當中,在開發完成一段代碼後若是負責任而不是說徹底把問題交給測試人員去發現的話,這個時候一般都會去作一些手動的測試。例如:前端
現代化的測試工具都在儘量的將這些人工的手動測試行爲抽象成代碼塊,當你有意識去進行手動測試的時候,其實已經開始在嘗試測試用例的行爲了。既然能夠經過手動的方式進行測試,那爲何還須要用代碼來實現測試?java
迴歸測試
,即在你修復 Bug 將你的系統從新測試一遍。可是若是你已經有了完善的測試用例了呢,直接執行命令搞定。在進入完善階段前,先說說你將如何實現測試用例。laravel
rubydescribe Meme do before do @meme = Meme.new end describe "when asked about cheeseburgers" do it "must respond positively" do @meme.i_can_has_cheezburger?.must_equal "OHAI!" end end describe "when asked about blending possibilities" do it "won't say no" do @meme.will_it_blend?.wont_match /^no/i end end end
上面的代碼來自於 Ruby 的 minitest。before
包含的代碼塊是在執行下面的測試用例前要作的事情,一般還會支持一個相對應的方法,在測試用例執行完執行。每一個用例裏面都進行一些很小的判斷。git
第一段中提到了一些手動測試裏面常常會涉及到的測試內容,這裏拿其中的 2
和 3
進行說明。在進行數據庫相關的測試時,須要在 before
中插入一條測試數據,而且在 after
中刪除測試數據。中間的測試用例中,經過執行相應的方法,執行完畢後:檢查數據變化狀況/檢查是否有預期的異常/是否返回預期結果 來確認代碼的正確性。若是是接口的話,就是經過代碼發起對應的請求,而後檢查返回的內容是否返回預期,有須要的話再去查看數據庫裏面的數據是否符合預期變化。github
如今已經有了測試用例,可是任然須要考慮一種特殊狀況。我如今爲一個函數寫了相對完善的測試用例了,跑完都 PASS
了,結果發現線上的日誌裏面仍是有那個函數的報錯。檢查下發現函數的某個分支以前在測試的時候沒有測試到,恰好線上的某種狀況運行到了這個分支,結果有一個很不明顯的語法錯誤報錯了,有沒有辦法能確保全部的代碼都測試過了?這裏須要引入的是一個叫作 測試用例覆蓋率 的概念,基本上每一個語言都會有響應的實現。經過測試用例覆蓋率,量化的告訴你你的測試用例有沒有跑完某某文件裏的全部代碼,而你須要作的,就是儘量保證你的覆蓋率保持在 100%。web
某種意義上來講,測試用例和測試覆蓋率是用來提升開發者對本身代碼自信心的工具。可是,他們也不是萬能的。測試用例裏面總可能會漏掉一些參數的可能性,固然你的代碼裏面也沒有爲這種可能性進行代碼的編寫,最終測試用例覆蓋率只能告訴你你寫的代碼咱們都幫你檢測過了測試過了,對於你沒有考慮到的可能性,表示無能爲力。因此儘量編寫嚴格的代碼,例如 javascript 裏面儘量都用 ===
而不是 ==
,使用強類型的編程規範等等,這些來下降這種由於接受的參數範圍過大帶來的潛在風險。數據庫
整個 Web (也不侷限於 web)一般包括三個層面的代碼 —— 單純數據處理與運算、涉及到數據庫、涉及到具體的網絡協議。其中單純的數據運算於處理主要爲普通的運算的函數或者是其餘代碼,涉及到數據庫就是傳統意義上 MVC
裏面的 M
,涉及到具體的網絡協議就是對應的 C
。這三塊的測試分別對應着第一節中常規的測試內容的前三條。編程
由於 C
層面一般還可能涉及到頁面的渲染以及相應協議的模擬,因此一般把測試的重心放在函數以及數據庫相關的代碼裏面能夠減小測試用例代碼的複雜度,這個就要求 Controller
的代碼要儘量少。對於複雜度較高的應用的一些目前的一些建議:後端
M
層,若是使用 Ruby 開發的話,ActiveRecord
以及Mongoid
都提供了很方便使用的 validation
功能。Pub/Sub
模式配合一些 ORM
中提供的鉤子(hook) 來實現 Model
之間的通訊。 例如在 A 建立的時候發佈某個消息,B監聽到消息以後修改他本身的某個屬性值。Command
模式將一些業務無關的功能從系統中抽離出來,例如郵件發送。以上的內容都避開了先後端須要聯調的測試用例,下面的內容主要是針對這塊。Ruby 在這個方向已經有一些比較優雅的實現,感興趣的能夠直接先去欣賞一下 Capybara。
隨着包括 Selenium Phantomjs 以及基於前者的 Watir 等一系列瀏覽器驅動的普及,使用代碼控制瀏覽器已經再也不是一件很複雜的事情。在這個能力的基礎上,能夠嘗試把基於前端的測試分爲四步:
基於這個流程,能夠解決絕大多數的前端測試。可是單純依靠這個流程任然不夠,由於頁面中可能出現例如驗證碼這樣的阻礙元素,在不修改代碼的前提下,能夠嘗試經過數據庫/緩存來取到這些內容。一樣,和測試接口相同,這裏也涉及到在測試前數據庫中插入測試數據,測試用例執行後嚴重數據庫裏面數據變化,以及所有測試完畢後刪除測試數據的內容。最終致使這塊測試用例代碼的實現須要同時對前端後端有必定的瞭解。目前還在考慮在借鑑 Capybara 的基礎上,設計出更加通用的方案。
最後貼一段 Capybara 的代碼結束這段內容:
rubyfeature "Signing in" do background do User.make(:email => 'user@example.com', :password => 'caplin') end scenario "Signing in with correct credentials" do visit '/sessions/new' within("#session") do fill_in 'Email', :with => 'user@example.com' fill_in 'Password', :with => 'caplin' end click_button 'Sign in' expect(page).to have_content 'Success' end given(:other_user) { User.make(:email => 'other@example.com', :password => 'rous') } scenario "Signing in as another user" do visit '/sessions/new' within("#session") do fill_in 'Email', :with => other_user.email fill_in 'Password', :with => other_user.password end click_button 'Sign in' expect(page).to have_content 'Invalid email or password' end end