【閱讀筆記】軟體構築美學(04)自動化測試

提升信心。評估既有的測試。整合既有的測試。自動執行測試。

書名:軟體構築美學

作者:Kyle Baley,Donald Belcham

譯者:蔡煥麟,張簡才祿

發行公司:悅知文化,精誠資訊股份有限公司

問題描述

程式的分支邏輯,每一條路徑都有可能出錯,所以都要測試。

確認程式碼是否正確以及各元件之間能否順利運作,這都是在撰寫新功能的時候都會碰到的問題。

1. 為了重現 Bug 花了很多時間。

2. 無法保證不會改東壞西。

 

活在改變的恐懼之中

信心是測試的主要理由。

沒有測試的程式,就像馬戲團的空中飛人沒有安全網。

沒有測試,你就會變成猶豫不決,然後心不甘情不願,最後對修正 Bug新增功能產生強烈的恐懼感

重拾信心與控制

修正 Bug 的第一步,先把問題範圍縮小,建議的方法: 先撰寫測試

我們目的要顯現目前無法順利執行的地方,所以要撰寫的是應該通過、卻未通過的測試

測試無法通過,產生紅燈。想辦法解決它,讓它變成綠燈

Bug 存活時間越久,處理的成本就越高。

降低成本的做好方法,就是在開發之前找出問題。

自動化測試幫得上忙,執行這些測試讓我們更有信心,也能確定程式不會因為修正某個問題而引發更多新問題

為什麼需要測試

1. 確保 Bug 不會重複出現

2. 確保新的程式碼達到可接受的品質水準

3. 建立與保持團隊和用戶的信心

4. 最重要的,加速回饋循環

任何新撰寫的程式碼都要有測試的保護,才能簽入。

修正 Bug 的時候,至少要有一個測試能夠確保同樣的 Bug 不會再出現。

沒有撰寫足夠的單元測試,就不要加入新功能。

 

自動化測試的種類

何謂單元?

單元就是能夠測試的最小範圍的程式碼

基於狀態的測試

最基礎也最直接的測試,就是基於狀態的測試(state-based)

主要是在確認物件、物件的屬性,或變數是否符合你預期的狀態

設定輸入參數 -> 預測試的方法 -> 比較預期結果與實際輸出結果

基於狀態的測試,不需要知道受測程式的內部運作方法

3A原則

Arrage:設定該測試所需的輸入資料。

Act:執行我們所要測試的動作。

Assert:驗證執行結果是否符合預期。

為測試命名

別害怕測試的名稱取太長。

一個好的測試名稱,光從名稱就能了解該測試的用途,以及在測什麼。

基於狀態的測試,通常也稱為黑箱測試(black box test),因為我們只看到輸入輸出

基於互動的測試

用來測試兩個或多個物件彼此的互動方式

1. 定義測試目標

2. 建立原型物件

3. 設定原型的預期結果

4. 執行程式碼

原型物件(mock)只是一種用來取代真正實作的代理物件

我們並不在乎 Repository 在測試情境中的運作方式,只關心 Service 物件。

不管去存取資料庫,檔案系統,還是網路服務,這需都與我們的測試目標無關。

我們只在乎 Service 物件在某個時間點必須呼叫這個方法。

越早開始使用 Mock 框架,節省的時間越多。

基於規格的測試

以比較接近自然語言的方法來描述測試的一種方式,規格直接由使用者故事而來。

作為一名客服人員

我可以儲存訂單

以便讓餐廳處理這份訂單

這種測試通常只對描述行為比較有用,而不是拿來驗證既有程式碼的正確性。

 

(重新)整合既有的測試專案

將既有的測試專案整合至開發流程的步驟

Step 1. 檢驗測試專案

Step 2. 處理測試

Step 3. 將單元測試與整合測試分開

Step 4. 重新驗證測試

Step 5. 整合至建置程序

檢驗既有的測試專案

分析既有的測試專案的第一步,就是看他能不能通過編譯。

編譯過不代表正確,還有其他問題要考慮。

發現無效的測試,有三種處理方式

1. 刪除:測試程式已經過時,就把他從測試專案中刪除

2. 重構:如果這個測試還有用處考慮將之重構。但通常刪掉重寫比較快。

3. 忽略:如果某個測試還有實際價值,只是目前無法執行或需要一些修改,可以考慮忽略它(跳過不執行)。

一旦有一個開始爛,很快就會有第二個:別忽略你的測試太久

分析既有的測試專案,並將它們重新整合至專案

即使所有重新整合進來的測試都能夠順利執行,有些也可能已經失去驗證的功能。

處理既有的測試

無法有效運作的單元測試,通常都會落入三大分類

1. 測試對象不是一個單元

2. 測試期望不具實質意義

3. 測試沒有反應商業需求

測試對象不是一個單元

你的測試所要驗證的對象超過一個單元

比如說,有個測試,它不只要檢查某客戶的資料是否正確,還要檢查它是否有存入資料庫。

與其嘗試在單一測試中完成多項測試目標,不如將其中的每一塊(或執行路徑)拆成獨立的測試來得簡單許多。

利用基於狀態的測試來驗證每個程式單元,讓你確認每一條路徑是否符合預期

如果程式切得夠細,驗證所有執行路徑的工作就會容易許多。

如果你發現在真正執行到受測的程式碼之前,有許多前置的設定工作,這可能就是爛測試的徵兆。

另外一種可能,寫程式的時候沒有遵循單一責任原則

測試期望不實質意義

測試的期望與檢查的項目應該要力求明確、嚴謹

含糊不清的測試,往往會持續蔓延而難以控制。

測試沒有反映商業需求

整個專案裡面可能就只有測試程式用到這個功能

這些沒用處的程式碼與測試,多少都會增加軟體專案在維護方面的負擔。

建議都將它刪除

將單元測試與整合測試分開

由於整合測試的執行時間比單元測試長,因此將他們分開便能夠讓整個測試執行的過程更加平順

建議專案目錄至少要有兩個,一個用來存放整合測試,一個用來存放單元測試。

無論以什麼方式組織你的測試,你都應該把這些測試當成一種顯現程式碼目前未來潛在問題的一種方式。

重新檢視你的測試

測試失敗時,注意那些經常錯誤的地方,並把這些錯誤的資訊記錄下來,以便日後找機會將他們解決。

在建置腳本中執行測試

最後一個步驟,要有一個建制程序來執行那些測試。

 

加入新的測試專案

加入單元測試與整合測試專案。

 

撰寫新測試

測試既有程式碼

撰寫測試來確保修正的程式正確無誤之前,你都不應該去修改應用程式

先別擔心要怎麼測試目前的功能,你應該要關注的是如何撰寫測試來驗證你打算修改的程式

測試新撰寫的程式

一般來說,傳統的 TAD(test-after development)先開發後測試,是最普遍的。

TTD(test-driven design)測試驅動設計更是相當值得採用的實務作法

測試驅動設計

使用 TDD 可以讓你依事先規劃的設計來撰寫程式

無論你決定先寫測試還是先寫程式,重點在於一定要寫測試,而且他們要能夠由自動化建置程序來執行

 

整合測試

需要知道程式從某一端到另一端的執行過程是否正確,例如:從使用者介面(UI)到資料庫端。

整合測試通常比單元測試容易撰寫,你不需要花太多心思去想那些元件該如何互動,

但它通常有不少準備與設定工作。

讓使用者來撰寫單元測試

越來越多人支持讓使用者一起參與軟體開發流程。

其中一個就是讓使用者積極參與驗收測試的撰寫工作

 

使用者介面測試

自動化建置最難處理的部份,就是使用者和應用程式最常接觸的部份:使用者介面。

測試網頁應用程式時有許多因素需要考量,其中一個就是支援多種瀏覽器的問題

用於自動化網頁瀏覽器的 Selenium 就是個好用的工具。

管理你的資料庫

許多應用程式都需要一個集中式資料庫。

結果就是免不了兩個人會同時處理同一個地方的資料。

如果可能的話,讓每個開發人員各自擁有一個資料庫複本

自動產生資料庫

在整合測試時,最好是能夠讓它們使用乾淨的資料庫,重頭建立。

為整合測試準備資料的程序。

建立資料庫 -> 加入測試資料 -> 執行測試 -> 刪除資料庫

處理既有的資料庫

由於企業資料本身易於變動的特性,使得自動化測試所需要的一致性更不容易維持。

請務必謹慎規劃你的資料庫測試策略,因為製造問題總是比解決問題容易。

 

總結

自動化測試能夠大幅提升團隊成員對程式碼的信心

單元測試(基於狀態的測試與基於互動的測試),後者需要用到 Mock 物件

單元測試與整合測試要分開。

發現無效的測試該如何處理(重構刪除、或忽略)。

發現程式有 Bug 或是準備撰寫新功能先撰寫測試程式

藉由撰寫測試程式,你就能夠確認應用程式是否有依你所想的方式運作