編寫軟體時,您可以透過測試確認運作是否正常運作。測試可大致定義為以特定方式執行軟體的程序,以確保測試正常運作。
測試成功後,您就能確保在新增程式碼、功能,或甚至升級依附元件時,已編寫的軟體將會繼續按照預期方式運作。測試也可以協助保護您的軟體,防範罕見情況或非預期的輸入。
以下列舉幾個建議您測試的網頁行為:
- 確保當使用者按下按鈕時,網站的功能運作正常。
- 確認複雜函式能產生正確的結果。
- 完成需要使用者登入的動作。
- 檢查表單是否在輸入格式錯誤的資料時正確回報錯誤。
- 確保複雜的網頁應用程式可以在使用者頻寬極低或離線時繼續運作。
自動與手動測試
測試軟體的一般方式有兩種:自動化測試和手動測試。
手動測試涉及真人直接執行軟體,例如在瀏覽器中載入網站,並確認網站可正常運作。手動測試很容易建立或定義,例如網站能否載入?您能執行這些操作嗎?—但每次執行產生的費用都會耗費大量人類時間。雖然人類的創意相當自由,可以啟用稱為「探索測試」的測試類型,但我們仍難免會發現失敗或不一致的問題,特別是在執行相同的工作多次時。
自動化測試是指任何可由電腦編寫程式碼及執行測試的程序,可確認軟體行為,「不必」執行任何重複步驟 (例如設定或檢查結果)。重要的是,自動測試設定完成後,即可頻繁執行。這仍然是非常廣泛的定義,值得注意的是,自動化測試能處理各種形狀和形式。本課程大部分以自動化測試為例
手動測試確實有其職責,通常是編寫自動化測試的前身,但自動測試變得不可靠、範圍廣泛或難以寫入。
透過範例掌握基礎概念
對我們來說,編寫 JavaScript 或相關語言的網頁開發人員來說,簡潔的自動化測試可以是類似每天執行的指令碼,例如透過 Node 執行,或透過瀏覽器載入:
import { fibonacci } from "../src/math.js";
if (fibonacci(0) !== 0) {
throw new Error("Invalid 0th fibonacci result");
}
const fib13 = fibonacci(13);
if (fib13 !== 233) {
throw new Error("Invalid 13th fibonacci result, was=${fib13} wanted=233");
}
這個簡單的範例提供了下列深入分析:
此為「測試」,因為它會執行一些軟體 (Fibonacci 函式),並檢查結果與預期值,確保其行為符合預期的方式運作。如果行為不正確,就會引發錯誤,JavaScript 會擲回
Error
來表達。即使您是在終端機或瀏覽器中手動執行這個指令碼,仍屬於自動測試,因為您不需要執行任何個別步驟,就可以重複執行測試。下一頁 (測試執行位置) 將進一步說明更多相關資訊。
雖然這項測試並未使用任何程式庫 (可在任何位置執行的 JavaScript),目前仍在測試中。有許多工具可協助您編寫測試 (包括本課程稍後會介紹的工具),但這些工具仍可用於執行發生問題時造成錯誤的基本原則。
實際測試程式庫
大多數程式庫或內建測試架構都提供兩個主要基本功能,讓測試更容易撰寫:斷言和定義獨立測試的方式。我們會在下一節「斷言和其他基元」中詳細說明這些概念。但整體來說,請務必記得,您看到或寫入的所有測試,最終都會使用這些基元。
斷言是一種結合檢查結果的方式,可在發生問題時導致錯誤。舉例來說,您可以導入 assert
,讓先前的測試更簡潔:
import { fibonacci } from "../src/math.js";
import { assert } from "a-made-up-testing-library";
assert.equal(fibonacci(0), 0, "Invalid 0th fibonacci result");
assert.equal(fibonacci(13), 233, "Invalid 13th fibonacci result");
您可以定義獨立的「測試」,進一步改善這項測試,可以選擇歸類為「套件」。以下套件獨立測試 Fibonacci 函式和 Catalan 函式:
import { fibonacci, catalan } from "../src/math.js";
import { assert, test, suite } from "a-made-up-testing-library";
suite("math tests", () => {
test("fibonacci function", () => {
assert.equal(fibonacci(0), 0, "Invalid 0th fibonacci result");
assert.equal(fibonacci(13), 233, "Invalid 13th fibonacci result");
});
test("relationship between sequences", () => {
const numberToCheck = 4;
const fib = fibonacci(numberToCheck);
const cat = catalan(numberToCheck);
assert.isAbove(fib, cat);
});
});
在軟體測試中,「test」代表名詞是指「測試案例」:一個獨立且可定址的情境,例如上一個範例中的「序列之間的關聯」測試案例。
個別命名的測試對於下列工作等工作相當實用:
- 判斷測試在一段時間內的成功或失敗情況。
- 依名稱標示錯誤或情境,以便更輕鬆地測試情境是否已解決。
- 執行部分測試,與其他測試分開執行,例如透過 glob 篩選器。
測試測試案例的其中一種方法是使用「三個 A」單元測試:安排、行動和斷言。每個測試案例的核心都會:
- 安排部分值或狀態 (這可能是硬式編碼的輸入資料)。
- 執行動作,例如呼叫方法。
- 斷言輸出值或更新狀態 (使用
assert
)。
測試規模
上一節的程式碼範例描述的是單元測試,因為它們測試軟體的次要部分,通常專注於單一檔案,在本例中,僅指單一函式的輸出內容。隨著您考量多個檔案、元件,甚至是不同互連系統的程式碼 (有時並非由您控制,例如網路服務或外部依附元件的行為),測試的複雜度會逐漸增加。因此,測試類型通常是根據其「範圍」或「scale」來命名。
除了單元測試外,還有一些其他測試類型的範例包括元件測試、視覺測試和整合測試。這些名稱沒有嚴格的定義,而且會因程式碼集而有不同的含義,因此請記得將這些名稱當做指南,並想出適合您的定義。舉例來說,您的系統中有哪些元件測試中?對於 React 開發人員,這可能實際上會對應至「React 元件」,但在其他情況下,開發人員可能有不同的意義。
個別測試的規模可將此概念放在一個概念內 (通常稱為「測試金字塔」),是有關測試檢查和執行方式的實用經驗法則。
這個概念已經過重溫,現在各種其他形狀也廣受歡迎,例如測試鑽石或測試冰錐。測試寫入的優先順序可能專屬於程式碼集。不過常見功能是較簡單的測試 (例如單元測試) 的執行速度較快、更容易寫入 (因此會產生更多測試),以及測試範圍有限;然而,編寫複雜的測試 (如端對端測試) 並不容易編寫,但可測試更廣泛的範圍。事實上,許多測試「形狀」的頂層通常是手動測試,因為有些使用者互動行為太複雜,無法轉換成自動化測試。
這些類型將在自動化測試類型中擴張。
隨堂測驗
大部分測試程式庫和架構提供哪些基本功能?
assert()
及其變化版本往往會納入,因為檢查較容易撰寫。test()
方法幾乎包含所有測試執行器。這一點很重要,因為測試程式碼不會在檔案的頂層執行,導致測試執行器將每個測試案例都視為獨立的單元。