113 lines
2.7 KiB
Markdown
113 lines
2.7 KiB
Markdown
|
|
# /tdd — 測試驅動開發 (Test-Driven Development)
|
|||
|
|
|
|||
|
|
使用**測試驅動開發 (TDD)** 模式實作功能。在撰寫任何邏輯代碼之前,先撰寫失敗的測試。
|
|||
|
|
|
|||
|
|
## 流程
|
|||
|
|
|
|||
|
|
1. **規劃 (Plan)**:定義功能需求與介面。
|
|||
|
|
2. **紅燈 (Red)**:撰寫一個會失敗的測試(因為實作尚未存在)。
|
|||
|
|
3. **綠燈 (Green)**:撰寫最少量的實作程式碼以使測試通過。
|
|||
|
|
4. **重構 (Refactor)**:清理程式碼同時保持測試通過。
|
|||
|
|
5. **重複 (Repeat)**:進行下一個小型測試。
|
|||
|
|
|
|||
|
|
## 使用範例
|
|||
|
|
|
|||
|
|
```bash
|
|||
|
|
/tdd 實作一個處理使用者註冊並驗證電子郵件的函式
|
|||
|
|
/tdd 建立一個具有 push, pop 和 peek 功能的堆疊 (Stack) 資料結構
|
|||
|
|
/tdd 為現有的 API 新增一個計算折扣的端點 (Endpoint)
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
## Go 語言範例流程
|
|||
|
|
|
|||
|
|
````markdown
|
|||
|
|
## 步驟 1:定義需求
|
|||
|
|
實作一個電子郵件驗證器,檢查是否為空且格式是否正確。
|
|||
|
|
|
|||
|
|
## 步驟 2:紅燈 — 撰寫失敗的測試
|
|||
|
|
建立 `validator_test.go`:
|
|||
|
|
|
|||
|
|
```go
|
|||
|
|
func TestValidateEmail(t *testing.T) {
|
|||
|
|
err := ValidateEmail("")
|
|||
|
|
if err == nil {
|
|||
|
|
t.Error("預期空字串會回報錯誤,但結果為 nil")
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
## 步驟 3:綠燈 — 使測試通過
|
|||
|
|
建立 `validator.go`:
|
|||
|
|
|
|||
|
|
```go
|
|||
|
|
func ValidateEmail(email string) error {
|
|||
|
|
if email == "" {
|
|||
|
|
return errors.New("email cannot be empty")
|
|||
|
|
}
|
|||
|
|
return nil
|
|||
|
|
}
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
## 步驟 4:重構
|
|||
|
|
將錯誤定義為具名的常數。
|
|||
|
|
|
|||
|
|
```go
|
|||
|
|
var (
|
|||
|
|
ErrEmailEmpty = errors.New("email cannot be empty")
|
|||
|
|
ErrEmailInvalid = errors.New("email format is invalid")
|
|||
|
|
)
|
|||
|
|
|
|||
|
|
func ValidateEmail(email string) error {
|
|||
|
|
if email == "" {
|
|||
|
|
return ErrEmailEmpty
|
|||
|
|
}
|
|||
|
|
if !emailRegex.MatchString(email) {
|
|||
|
|
return ErrEmailInvalid
|
|||
|
|
}
|
|||
|
|
return nil
|
|||
|
|
}
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
## 步驟 5:執行測試 — 驗證通過
|
|||
|
|
```bash
|
|||
|
|
$ go test ./validator/...
|
|||
|
|
PASS
|
|||
|
|
ok project/validator 0.003s
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
## 步驟 6:檢查覆蓋率
|
|||
|
|
```bash
|
|||
|
|
$ go test -cover ./validator/...
|
|||
|
|
PASS
|
|||
|
|
coverage: 100.0% of statements
|
|||
|
|
```
|
|||
|
|
````
|
|||
|
|
|
|||
|
|
## 測試模式
|
|||
|
|
|
|||
|
|
### 表格驅動測試 (Table-Driven Tests)
|
|||
|
|
```go
|
|||
|
|
tests := []struct {
|
|||
|
|
name string
|
|||
|
|
input InputType
|
|||
|
|
want OutputType
|
|||
|
|
wantErr bool
|
|||
|
|
}{
|
|||
|
|
{"case 1", input1, want1, false},
|
|||
|
|
{"case 2", input2, want2, true},
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
for _, tt := range tests {
|
|||
|
|
t.Run(tt.name, func(t *testing.T) {
|
|||
|
|
got, err := Function(tt.input)
|
|||
|
|
// 斷言 (assertions)
|
|||
|
|
})
|
|||
|
|
}
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
## 規則
|
|||
|
|
- **測試先行**:絕對不在撰寫測試之前撰寫實作程式碼。
|
|||
|
|
- **最小實作**:僅撰寫足以通過目前測試的程式碼。
|
|||
|
|
- **持續驗證**:在每個步驟之後執行所有測試。
|
|||
|
|
- **覆蓋率理想值**:目標為新程式碼達到 100% 覆蓋率。
|