iT邦幫忙

2025 iThome 鐵人賽

DAY 27
0
Software Development

重啟挑戰:老派軟體工程師的測試修練系列 第 27

Day 27 – GitHub Copilot 測試實戰:AI 輔助測試開發指南

  • 分享至 

  • xImage
  •  

現在看到的內容是第二版,與之前所發佈的第一版內容有極大的差異。
主要是第一版的內容過於空泛和不切實際,無法真的拿來應用在實際的專案中。
所以我重新整理了內容,並且加入了更多實際可操作的指令範本和步驟。

前言

現在 GitHub Copilot 已經成為許多開發者不可或缺的 AI 助手,但你是否發現在測試開發上,它產生的程式碼品質參差不齊?有時候格式不統一、命名不規範,甚至測試邏輯有問題?

或者是看到很多其他公司團隊會透過 Github Copilot 或其他 AI 工具來達到自動產出測試程式碼,開發團隊也躍躍欲試,但是團隊從來沒有在開發流程導入過單元測試的經驗,面臨到不知道應該怎麼讓 AI 工具幫忙產出符合團隊標準的測試程式碼。

以往我在帶新人學習單元測試的時候,並不會讓新人直接去對著他們的練習專案寫測試,而是會先讓他們把練習專案的程式碼調整為可測試的程式碼,然後再帶著他們整理出程式碼的 使用案例,藉此瞭解他們自己程式的 業務邏輯 是什麼。

因為很多人不知道該怎麼寫單元測試的第一個難題就是程式碼不可測,接著第二個難題就是不知道怎麼把自己所寫的程式碼整理出使用案例。雖然聽起來很荒謬,但這是在很多開發人員身上都會有的狀況,因為他們可以知道程式邏輯會怎麼執行,但是卻無法整理出清楚的業務邏輯和使用案例。

所以整理出清楚的業務邏輯和使用案例之後,才會知道有多少的情境是需要測試的,然後就可以開始著手進行單元測試的練習與撰寫。經過多次的來回練習與實作後,新人們就會知道如何對自己所寫的程式碼進行測試的撰寫。

而現在是變成要讓 Github Copilot 協助我們一同來完成這樣的過程:分析業務邏輯設計測試情境產生測試程式碼。雖然我這邊提供的指令和設定不一定適合你的專案,但至少可以給你一個參考方向,讓你可以整理出一套能夠適合開發團隊的做法與流程。

為什麼需要通用測試指令範本?

問題場景

  • 每次請 Copilot 寫測試都要重新解釋需求
  • 每次產生的測試情境格式都不同
  • 產生的測試命名不一致,有英文有中文
  • 測試程式碼結構不統一,有些有 3A Pattern,有些沒有

解決方案

  • 3 個通用指令範本:可應用於任何專案的參數化指令
  • 標準化配置:透過 copilot-instructions.md 確保一致性
  • 階段式學習:從簡單到複雜,循序漸進掌握技巧

實驗架構:三階段漸進式練習

我們用三個不同複雜度的範例來實驗這套方法:

階段一:基礎業務邏輯測試

  • 實驗重點:測試指令範本的基本運用
  • 範例場景:純邏輯計算、輸入驗證、邊界值測試
  • 範例專案DiscountCalculator 折扣計算服務
  • 學習目標:理解業務邏輯分析 → 測試情境設計 → 程式碼產生的流程

階段二:依賴注入測試技巧

  • 實驗重點:處理外部依賴的測試指令
  • 範例場景:服務依賴、資料庫存取、外部 API 呼叫測試
  • 範例專案UserService 使用者服務
  • 學習目標:了解如何讓 AI 處理 Mock 物件的建立與管理

階段三:業務流程測試應用練習

  • 實驗重點:多步驟業務流程的測試設計
  • 範例場景:多步驟業務流程、狀態轉換、異常處理測試
  • 範例專案OrderProcessor 訂單處理流程
  • 學習目標:探索複雜業務規則與多依賴協調的測試方法

準備工作

在開始學習 GitHub Copilot 測試技巧之前,需要先完成環境準備工作。

安裝 xUnit.v3 測試範本

由於本教學使用 xUnit.v3 作為測試框架,建議先安裝官方的專案範本,方便後續建立新的測試專案:

dotnet new install xunit.v3.templates

安裝成功後的輸出範例

將安裝下列範本套件:
   xunit.v3.templates

xunit.v3.templates (版本 3.0.1) 已安裝,將會以 最新版本 取代。
xunit.v3.templates::3.0.1 已成功解除安裝。
成功: xunit.v3.templates::3.0.1 已安裝下列範本:
範本名稱                        簡短名稱          語言        標記
------------------------------  ----------------  ----------  ----------
xUnit.net v3 Extension Project  xunit3-extension  [C#],F#,VB  Test/xUnit
xUnit.net v3 Test Project       xunit3            [C#],F#,VB  Test/xUnit

範本用途說明

  • xunit3:用於建立 xUnit.v3 測試專案
  • xunit3-extension:用於建立 xUnit.v3 擴充專案

使用範例

# 建立新的 xUnit.v3 測試專案
dotnet new xunit3 -n MyProject.Tests

下載範例專案

完成範本安裝後,下載完整的範例專案:

# 複製專案
git clone https://github.com/kevintsengtw/30Days_in_Testing_Samples.git

# 進入 Day27 範例專案
cd 30Days_in_Testing_Samples/day27

# 建置專案
dotnet build Day27.Samples.sln

專案結構說明

範例專案包含完整的專案結構和業務邏輯類別,但測試專案是空的,需要透過學習過程中的指令範本來建立測試程式碼。

使用限制與注意事項

測試環境說明:這篇文章的範例是基於 Claude Sonnet 4 模型測試的結果。你在實際操作時可能會遇到:

  • 不同的輸出格式:各種 AI 模型(GPT-4、Claude、Gemini ... 等等其他)的回應風格不同
  • 結果不一致:即使用相同指令,AI 的回應也可能因為上下文或模型更新而有差異
  • 需要調整:這裡提供的 copilot-instructions.md 和指令範本,在你的專案中可能需要大幅修改才能使用

使用建議

  • 重點在思路:這篇文章的價值不在於提供完美的指令,而是讓你了解這種系統化方法的可能性
  • 需要迭代:把這些指令當作起點,根據你的團隊需求和 AI 回應結果持續調整
  • 保持驗證:AI 產生的任何內容都需要人工檢查和驗證

階段一:基礎業務邏輯測試技巧

步驟 1-1:設定階段一的 copilot-instructions.md

首先,我們需要建立專案的 GitHub Copilot 指導設定檔。這是整個技巧的核心 - 讓 GitHub Copilot 了解我們的專案規範。

開啟 .github/copilot-instructions.md,你會看到以下內容:

# GitHub Copilot 測試開發指導

## 開發環境資訊
- .NET 9
- 測試框架:xUnit.v3 (3.0.1)
  - NuGet Packages 安裝:
    - xunit.v3
    - xunit.runner.visualstudio
- 斷言庫:AwesomeAssertions (不使用 FluentAssertions)

## 測試檔案組織結構
- 測試專案命名:{ProjectName}.Tests
- 測試類別命名:{ClassName}Tests

## 測試方法命名規範(重要)
- **測試方法名稱必須使用繁體中文**
- 命名格式:方法名_測試情境_預期結果
- 範例:
  - CalculateDiscountedPrice_輸入有效價格和折扣率_應回傳正確折扣價格()
  - CalculateDiscountedPrice_輸入負數原始價格_應拋出ArgumentException()

## DisplayName 使用規範(重要)
- **每個測試方法都必須在 [Fact] 屬性中指定 DisplayName 參數**
- DisplayName 格式:`方法功能描述: 情境描述,預期結果`
- 範例:
  [Fact(DisplayName = "計算折扣後的價格: 輸入有效的價格和折扣率,應回傳正確的折扣價格")]
  public void CalculateDiscountedPrice_輸入有效價格和折扣率_應回傳正確折扣價格()

## 測試程式碼規範
- 所有測試必須遵循 AAA 模式 (Arrange-Act-Assert)
- 必須標註 // Arrange, // Act, // Assert 三個區塊
- 使用 AwesomeAssertions 的 Should() 語法進行斷言
- 變數名稱使用英文,類別、方法、屬性註解使用繁體中文

## 程式碼風格要求
- 使用繁體中文進行測試方法命名和 DisplayName
- 類別、建構式、屬性、方法以及程式註解一律使用繁體中文
- 變數名稱、函數名稱、類別名稱一律使用英文
- if 判斷式一定要有大括號 {}, 即使只有一層也不能省略
- 一個類別就是一個檔案,不要所有類別都放在同一個 cs 檔案裡

關鍵要點

  • 這個檔案告訴 GitHub Copilot 我們的專案規範
  • 確保產生的測試程式碼符合團隊標準
  • 可以根據不同專案需求調整配置

GitHub Copilot Chat 操作模式說明

在接下來的學習中,我們統一使用 GitHub Copilot Chat 的 Agent 模式

  • Agent 模式:自動建立檔案並寫入完整程式碼,無需手動複製貼上
  • 優勢:一個指令完成所有操作,提高學習效率
  • 適用範圍:從分析文件到測試程式碼產生

步驟 1-2:學習通用業務邏輯分析指令

現在我們來學習第一個通用指令範本。這個指令可以應用到任何業務邏輯方法的分析。

通用指令範本:業務邏輯分析與儲存

請為 `[檔案路徑]/[類別名稱].cs` 中的 `[方法名稱]` 方法進行測試分析,並將結果儲存為 `[類別名稱]_TestScenario.md` 檔案:

**檔案標題格式**:# [類別名稱] 測試情境分析

**內容結構**(請嚴格按照以下編號和標題):

## [方法名稱] 方法

### 1. 方法簽名分析
列出參數類型、回傳類型、是否為非同步

### 2. 業務邏輯分析
識別主要的邏輯分支和驗證規則

### 3. 測試情境建議
涵蓋正常情境、邊界條件、異常處理的核心測試情境,每個情境包含具體輸入值、預期結果、驗證重點

### 4. 依賴關係識別
列出需要 Mock 的外部依賴(如有),如果是純函數請註明無外部依賴

**重要限制**:
- 只產生上述 4 個區段,不要增加其他內容
- 不要包含測試實作建議、測試覆蓋率目標等額外內容
- 依賴關係分析請專注於外部服務、資料庫、其他類別等,不要提及 .NET Framework 內建功能

請將完整分析結果儲存到適合您專案結構的目錄中的 `[類別名稱]_TestScenario.md` 檔案。

範例應用

  • Day27 學習範例:分析 DiscountCalculator.CalculateDiscountedPrice 並儲存為 DiscountCalculator_TestScenario.md
  • 實際專案應用:將 [檔案路徑]、[類別名稱]、[方法名稱] 替換為專案的實際值

操作練習:分析業務邏輯並產生測試情境

在 VS Code 中開啟 GitHub Copilot Chat,切換到 Agent 模式,輸入以下指令:

請為 `src/Day27.Core/Services/DiscountCalculator.cs` 中的 `CalculateDiscountedPrice` 方法進行測試分析,並將結果儲存為 `DiscountCalculator_TestScenario.md` 檔案:

**檔案標題格式**:# DiscountCalculator 測試情境分析

**內容結構**(請嚴格按照以下編號和標題):

## CalculateDiscountedPrice 方法

### 1. 方法簽名分析
列出參數類型、回傳類型、是否為非同步

### 2. 業務邏輯分析
識別主要的邏輯分支和驗證規則

### 3. 測試情境建議
涵蓋正常情境、邊界條件、異常處理的核心測試情境,每個情境包含具體輸入值、預期結果、驗證重點

### 4. 依賴關係識別
列出需要 Mock 的外部依賴(如有),如果是純函數請註明無外部依賴

**重要限制**:
- 只產生上述 4 個區段,不要增加其他內容
- 不要包含測試實作建議、測試覆蓋率目標等額外內容
- 依賴關係分析請專注於外部服務、資料庫、其他類別等,不要提及 .NET Framework 內建功能

請將完整分析結果儲存到 `docs/testing/DiscountCalculator_TestScenario.md` 檔案中。

預期產出和檔案內容

GitHub Copilot 會產生包含測試情境清單的分析結果,將完整結果儲存到 DiscountCalculator_TestScenario.md

不同 AI 模型的差異

  • 不同的 AI 模型(GPT-4、Claude、Gemini 等)會產生不同格式的分析結果
  • 即使相同模型,也可能因為輸入的細微差異而產生不同的測試情境
  • 重點是確保涵蓋完整的測試情境,而不是追求格式的一致性

檔案內容組織建議
無論 AI 產生什麼格式,建議測試情境檔案包含以下要素:

  1. 方法基本資訊:參數、回傳類型、業務邏輯摘要
  2. 測試情境分類
    • 正常情境:典型的使用案例和有效輸入
    • 邊界條件:極值、臨界值測試
    • 異常處理:無效輸入、錯誤條件
  3. 具體測試數據:每個情境的輸入值和預期結果
  4. 依賴關係:需要 Mock 的外部依賴(如有)

常見問題處理

  1. 格式調整:如果產生的標題結構不正確,手動調整為 # 類別名稱 測試情境分析## 方法名稱 方法
  2. 內容精簡:移除指令範圍外的內容(如測試實作建議、覆蓋率目標)
  3. 依賴關係修正:將 .NET Framework 等內建功能說明改為實際外部依賴

重要:這個階段的目標是獲得清晰且可重複使用的測試情境清單,Agent 模式會自動將結果儲存為 {類別名稱}_TestScenario.md 檔案。

檔案儲存建議

檔案命名{類別名稱}_TestScenario.md

儲存位置:建議放在 docs/testing/ 目錄下,方便團隊統一管理。

步驟 1-3:基於測試情境建立測試檔案與產生完整測試程式碼

有了步驟 1-2 產生的測試情境清單後,現在我們要學習如何將這些情境轉換為完整的測試程式碼。

通用指令範本:基於測試情境產生測試程式碼

請在測試專案中建立 `[類別名稱]Tests.cs` 檔案,並基於 `[類別名稱]_TestScenario.md` 中的測試情境產生完整的 xUnit 測試類別:

**重要**:請嚴格遵循專案的 copilot-instructions.md 規範

**測試情境來源**:請參考您的測試文件目錄中的 `[類別名稱]_TestScenario.md` 檔案中的測試情境清單

**程式碼要求**:
- 3A Pattern 並標註註解
- DisplayName 屬性
- 依據測試情境產生對應的測試方法

實際操作:產生 DiscountCalculator 測試程式碼

在 GitHub Copilot Chat 中,切換到 Agent 模式,輸入以下指令:

請在測試專案中建立 `DiscountCalculatorTests.cs` 檔案,並基於 `DiscountCalculator_TestScenario.md` 中的測試情境產生完整的 xUnit 測試類別:

**重要**:請嚴格遵循專案的 copilot-instructions.md 規範

**測試情境來源**:請參考您的測試文件目錄中的 `DiscountCalculator_TestScenario.md` 檔案中的 CalculateDiscountedPrice 方法測試情境清單

**程式碼要求**:
- 3A Pattern 並標註註解
- DisplayName 屬性
- 依據測試情境產生對應的測試方法

Agent 會自動建立測試檔案並寫入完整程式碼。

建置與測試驗證

產生測試程式碼後,必須進行建置和測試執行來驗證程式碼正確性:

使用 GitHub Copilot Chat Agent 模式

在 GitHub Copilot Chat 中,切換到 Agent 模式,輸入以下指令:

請執行以下步驟來建置專案並執行測試:

1. 建置整個解決方案
2. 執行剛產生的 CalculateDiscountedPrice 方法相關測試
3. 顯示測試結果摘要

請使用 FullyQualifiedName 篩選,只執行方法名稱包含 "CalculateDiscountedPrice" 的測試方法。

如果有建置錯誤或測試失敗,請說明問題並提供修正建議。

直接使用終端機指令

開啟 VS Code 終端機,在專案根目錄執行:

# 建置專案
dotnet build

# 方式 1:使用方法名稱前綴篩選 CalculateDiscountedPrice 相關測試
dotnet test --filter "FullyQualifiedName~CalculateDiscountedPrice" --verbosity normal

# 方式 2:使用 DisplayName 前綴篩選相同功能的測試
dotnet test --filter "DisplayName~計算折扣後的價格" --verbosity normal

# 方式 3:使用 FullyQualifiedName 篩選特定測試類別
dotnet test --filter "FullyQualifiedName~DiscountCalculatorTests" --verbosity normal

預期結果驗證

成功的測試執行應該顯示類似以下結果:

測試摘要: 總計: X, 失敗: 0, 成功: X, 已跳過: 0
測試執行時間: X.X 秒

篩選指令說明

  • 方式 1FullyQualifiedName~CalculateDiscountedPrice 會執行所有方法名稱包含「CalculateDiscountedPrice」的測試
  • 方式 2DisplayName~計算折扣後的價格 會執行所有 DisplayName 包含「計算折扣後的價格」的測試(推薦新方式)
  • 方式 3FullyQualifiedName~DiscountCalculatorTests 會執行整個 DiscountCalculatorTests 類別的所有測試

方式 2 的優勢

  • 語義清晰:使用中文業務功能描述,更直觀易懂
  • 業務導向:直接按照業務功能分組執行測試
  • 團隊友善:非程式開發人員也能理解測試內容

實際驗證範例

# 只執行「計算折扣價格」功能的測試方法
dotnet test --filter "DisplayName~計算折扣價格" --verbosity normal

常見問題處理

  1. 建置錯誤

    • 檢查 using 命名空間是否正確
    • 確認 AwesomeAssertions 套件是否已安裝
    • 檢查測試方法命名是否符合 C# 語法規範
  2. 測試失敗

    • 檢查測試邏輯是否與業務邏輯一致
    • 確認測試數據和預期結果是否正確
    • 檢查 3A Pattern 的實作是否正確
  3. 套件相關錯誤

    # 確認 AwesomeAssertions 套件版本
    dotnet list package
    
    # 如需要,重新安裝套件
    dotnet add package AwesomeAssertions --version 9.1.0
    

重要:只有通過建置和測試執行驗證,才能確保 GitHub Copilot 產生的測試程式碼真正可用且正確。

階段一學習成果驗證

完成階段一後,你應該能夠:

掌握的技巧

  1. 設定 GitHub Copilot 專案規範

    • 建立 copilot-instructions.md 確保程式碼一致性
    • 了解如何調整設定適應不同專案需求
  2. 3 個基礎測試指令範本

    • 業務邏輯分析指令
    • 測試情境產生指令
    • 測試程式碼產生指令
  3. 標準化測試開發流程

    • 系統性分析 → 情境設計 → 自動程式碼產生
    • 提高測試品質和開發效率

驗證練習

請使用學到的指令範本,為 DiscountCalculator 的另一個方法 CalculateBulkDiscount 產生測試:

練習步驟

  1. 使用指令範本 1-2 分析 CalculateBulkDiscount 方法

    請為 `src/Day27.Core/Services/DiscountCalculator.cs` 中的 `CalculateBulkDiscount` 方法進行測試分析,並將結果追加到現有的 `DiscountCalculator_TestScenario.md` 檔案中:
    
    **追加內容結構**(請嚴格按照以下編號和標題):
    
    ## CalculateBulkDiscount 方法
    
    ### 1. 方法簽名分析
    列出參數類型、回傳類型、是否為非同步
    
    ### 2. 業務邏輯分析
    識別主要的邏輯分支和驗證規則
    
    ### 3. 測試情境建議
    涵蓋正常情境、邊界條件、異常處理的核心測試情境,每個情境包含具體輸入值、預期結果、驗證重點
    
    ### 4. 依賴關係識別
    列出需要 Mock 的外部依賴(如有),如果是純函數請註明無外部依賴
    
    **重要限制**:
    - 只產生上述 4 個區段,不要增加其他內容
    - 不要包含測試實作建議、測試覆蓋率目標等額外內容
    - 依賴關係分析請專注於外部服務、資料庫、其他類別等,不要提及 .NET Framework 內建功能
    
    請將 CalculateBulkDiscount 方法的分析結果追加到現有的 `DiscountCalculator_TestScenario.md` 檔案中。
    
  2. 檢查分析結果

    • 確認 DiscountCalculator_TestScenario.md 檔案包含兩個方法的完整分析
  3. 使用指令範本 1-3 產生測試程式碼

    請在現有的 `DiscountCalculatorTests.cs` 檔案中,基於 `DiscountCalculator_TestScenario.md` 中的 CalculateBulkDiscount 方法測試情境,新增對應的測試方法:
    
    **重要**:請嚴格遵循專案的 copilot-instructions.md 規範
    
    **測試情境來源**:請參考 `DiscountCalculator_TestScenario.md` 檔案中的 CalculateBulkDiscount 方法測試情境清單
    
    **程式碼要求**:
    - 3A Pattern 並標註註解
    - DisplayName 屬性
    - 依據測試情境產生對應的測試方法
    
  4. 檢查程式碼規範:確認產生的程式碼是否符合 copilot-instructions.md 規範

  5. 驗證新的篩選功能:測試 DisplayName 前綴篩選功能

    # 測試「計算批量折扣」功能的測試篩選
    dotnet test --filter "DisplayName~計算批量折扣" --verbosity normal
    
    # 預期應該執行 CalculateBulkDiscount 相關測試
    

預期成果

  • DiscountCalculator_TestScenario.md 檔案包含兩個方法的完整分析
  • DiscountCalculatorTests.cs 檔案包含兩個方法的完整測試
  • 所有測試程式碼符合專案規範並通過測試執行
  • DisplayName 前綴篩選功能正常運作,可按業務功能分組執行測試

階段二:依賴注入測試技巧

在階段一中,我們學會了處理純業務邏輯的測試。現在進入階段二,學習處理包含依賴注入的業務服務測試。這是實際專案中最常見的測試場景。

學習重點

從簡單邏輯進階到服務層測試

  • Mock 物件:學會建立和管理外部依賴的替身
  • 非同步測試:處理 async/await 模式的業務邏輯
  • 依賴驗證:確認服務之間的互動是否正確
  • 業務規則測試:驗證複雜的業務邏輯流程

實務應用價值

  • 可應用於任何 Repository 模式的資料存取層測試
  • 適用於任何 Service 模式的業務邏輯層測試
  • 涵蓋大部分企業級應用的測試需求

步驟 2-1:更新 copilot-instructions.md(加入 Mock 框架設定)

首先,我們需要更新 GitHub Copilot 的指導設定,加入 Mock 框架的規範。

重要:階段二是在階段一的基礎上繼續添加內容,不是替換整個檔案。

.github/copilot-instructions.md 中進行以下更新:

1. 更新開發環境資訊中的 NuGet Packages

xunit.runner.visualstudio 之後新增一行:

    - NSubstitute (5.3.0)

2. 在檔案末尾添加階段二的新增規範

## Mock 物件設定規範
- 使用 Substitute.For<T>() 建立 Mock 物件
- 使用 Returns() 設定回傳值(包含非同步方法)
- 使用 Received() 驗證方法呼叫
- 使用 DidNotReceive() 驗證方法未被呼叫
- 使用 Throws() 模擬異常情況

## 依賴注入測試結構
- 測試類別建構式中初始化所有 Mock 物件
- Mock 物件設定為 private readonly 欄位
- Mock 物件命名:mock{ServiceName}(例:mockUserRepository)
- 在建構式中建立待測試的服務實例

## 依賴注入測試規範
- 非同步測試使用 async/await 模式
- 依賴互動驗證要完整且精確
- Mock 設定要精確,避免過度設定
- 注意測試的獨立性,避免測試間相互影響
- 涵蓋成功路徑、驗證失敗、業務規則、依賴失敗、邊界條件等測試情境

重要變更說明

  1. 加入 NSubstitute 框架規範:明確指定 Mock 物件的建立和設定方式
  2. 測試類別結構規範:定義測試類別的組織方式
  3. 依賴互動驗證:加入驗證服務間呼叫的規範
  4. 測試情境分類:系統性地涵蓋各種測試場景

步驟 2-2:學習通用業務邏輯分析指令(處理依賴注入服務)

階段二繼續使用與階段一相同的通用指令範本,但分析重點會自動調整到依賴注入服務的特性。

與階段一的一致性

  • 指令範本完全相同:確保學習一致性,避免學習負擔
  • 分析重點自然調整:第 4 點「依賴關係識別」會自動識別更多外部依賴
  • 測試情境自動豐富:GitHub Copilot 會根據依賴關係產生 Mock 相關的測試情境

實際操作:分析 UserService.RegisterUserAsync

在 GitHub Copilot Chat 中,切換到 Agent 模式,輸入以下指令:

請為 `src/Day27.Core/UserService.cs` 中的 `RegisterUserAsync` 方法進行測試分析,並將結果儲存為 `UserService_TestScenario.md` 檔案:

**檔案標題格式**:# UserService 測試情境分析

**內容結構**(請嚴格按照以下編號和標題):

## RegisterUserAsync 方法

### 1. 方法簽名分析
列出參數類型、回傳類型、是否為非同步

### 2. 業務邏輯分析
識別主要的邏輯分支和驗證規則

### 3. 測試情境建議
涵蓋正常情境、邊界條件、異常處理的核心測試情境,每個情境包含具體輸入值、預期結果、驗證重點

### 4. 依賴關係識別
列出需要 Mock 的外部依賴(如有),如果是純函數請註明無外部依賴

**重要限制**:
- 只產生上述 4 個區段,不要增加其他內容
- 不要包含測試實作建議、測試覆蓋率目標等額外內容
- 依賴關係分析請專注於外部服務、資料庫、其他類別等,不要提及 .NET Framework 內建功能

請將完整分析結果儲存到 `docs/testing/UserService_TestScenario.md` 檔案中。

階段二的分析結果特點

使用相同的指令範本分析 UserService,會產生以下特點的結果:

1. 方法簽名分析

  • 會識別出這是非同步方法 (async Task<User>)
  • 分析參數類型和業務意義

2. 業務邏輯分析

  • 識別 Email 重複性檢查邏輯
  • 密碼雜湊處理流程
  • 使用者物件建立和儲存流程
  • 歡迎郵件發送邏輯

3. 測試情境建議

  • 正常情境:有效輸入的成功註冊流程
  • 邊界條件:Email 格式邊界、密碼長度邊界
  • 異常處理:Email 重複、依賴服務失敗、參數驗證失敗

4. 依賴關係識別

  • IUserRepository:需要 Mock 的資料存取依賴
  • IEmailService:需要 Mock 的郵件服務依賴

步驟 2-3:基於測試情境建立測試檔案與產生完整測試程式碼

步驟 2-3:學習通用測試程式碼產生指令(含 Mock 物件)

階段二繼續使用與階段一相同的通用指令範本,GitHub Copilot 會根據更新的 copilot-instructions.md 自動產生包含 Mock 物件的測試程式碼。

與階段一的一致性

  • 指令範本完全相同:確保操作一致性,一套指令適用所有場景
  • 自動 Mock 處理:GitHub Copilot 會根據 copilot-instructions.md 中的 Mock 框架規範自動產生 Mock 物件
  • 依賴互動驗證:會自動包含對依賴方法呼叫的驗證

實際操作:產生 UserService 測試程式碼

在 GitHub Copilot Chat 中,切換到 Agent 模式,輸入以下指令:

請在測試專案中建立 `UserServiceTests.cs` 檔案,並基於 `UserService_TestScenario.md` 中的測試情境產生完整的 xUnit 測試類別:

**重要**:請嚴格遵循專案的 copilot-instructions.md 規範

**測試情境來源**:請參考您的測試文件目錄中的 `UserService_TestScenario.md` 檔案中的 RegisterUserAsync 方法測試情境清單

**程式碼要求**:
- 3A Pattern 並標註註解
- DisplayName 屬性
- 依據測試情境產生對應的測試方法

GitHub Copilot 的自動化處理

由於 copilot-instructions.md 已包含 Mock 框架規範,GitHub Copilot 會自動:

  1. 建立 Mock 物件

    private readonly IUserRepository mockUserRepository;
    private readonly IEmailService mockEmailService;
    private readonly UserService userService;
    
  2. 設定 Mock 行為

    // Arrange
    mockUserRepository.ExistsByEmailAsync("test@example.com").ReturnsAsync(false);
    mockUserRepository.SaveAsync(Arg.Any<User>()).ReturnsAsync(expectedUser);
    
  3. 驗證依賴互動

    // Assert
    await mockUserRepository.Received(1).ExistsByEmailAsync("test@example.com");
    await mockUserRepository.Received(1).SaveAsync(Arg.Any<User>());
    

步驟 2-4:建置與測試驗證

產生測試程式碼後,必須進行建置和測試執行來驗證程式碼正確性:

使用 GitHub Copilot Chat Agent 模式

在 GitHub Copilot Chat 中,切換到 Agent 模式,輸入以下指令:

請執行以下步驟來建置專案並執行測試:

1. 建置整個解決方案
2. 執行剛產生的 RegisterUserAsync 方法相關測試
3. 顯示測試結果摘要

請使用 FullyQualifiedName 篩選,只執行方法名稱包含 "RegisterUserAsync" 的測試方法。

如果有建置錯誤或測試失敗,請說明問題並提供修正建議。

直接使用終端機指令

開啟 VS Code 終端機,在專案根目錄執行:

# 建置專案
dotnet build

# 方式 1:使用方法名稱前綴篩選 RegisterUserAsync 相關測試
dotnet test --filter "FullyQualifiedName~RegisterUserAsync" --verbosity normal

# 方式 2:使用 DisplayName 前綴篩選相同功能的測試
dotnet test --filter "DisplayName~註冊新使用者" --verbosity normal

# 方式 3:使用 FullyQualifiedName 篩選特定測試類別
dotnet test --filter "FullyQualifiedName~UserServiceTests" --verbosity normal

預期結果驗證

成功的測試執行應該顯示類似以下結果:

測試摘要: 總計: X, 失敗: 0, 成功: X, 已跳過: 0
測試執行時間: X.X 秒

篩選指令說明

  • 方式 1FullyQualifiedName~RegisterUserAsync 會執行所有方法名稱包含「RegisterUserAsync」的測試
  • 方式 2DisplayName~註冊新使用者 會執行所有 DisplayName 包含「註冊新使用者」的測試
  • 方式 3FullyQualifiedName~UserServiceTests 會執行整個 UserServiceTests 類別的所有測試

重要:只有通過建置和測試執行驗證,才能確保 GitHub Copilot 產生的測試程式碼真正可用且正確。

階段二學習成果驗證

完成階段二後,你應該能夠:

掌握的技巧

  1. 設定依賴注入測試環境

    • 更新 copilot-instructions.md 加入 Mock 框架規範
    • 了解如何配置 NSubstitute 測試環境
  2. 3 個統一的測試指令範本

    • 業務邏輯分析指令(階段一、二通用)
    • 測試程式碼產生指令(階段一、二通用)
    • 建置與測試驗證(階段一、二通用)
  3. 標準化依賴注入測試開發流程

    • 依賴分析 → Mock 設定 → 測試產生 → 品質驗證
    • 處理非同步業務邏輯的測試技巧
    • 建立一致且高品質的服務層測試

驗證練習

UserService 還有三個公開方法尚未建立測試:UpdateUserStatusAsyncValidatePasswordIsValidEmail。請使用學到的指令範本,為這三個方法產生完整的測試:

練習目標

  1. 完整的 UserService 測試覆蓋

    • 目前已有 RegisterUserAsync 的測試
    • 需要補齊其餘三個方法的測試
  2. 混合測試類型練習

    • UpdateUserStatusAsync:非同步方法,包含依賴注入
    • ValidatePassword:同步方法,純邏輯驗證
    • IsValidEmail:同步方法,純邏輯驗證
  3. 階段整合驗證

    • 驗證階段一和階段二指令範本的整合應用
    • 確認 copilot-instructions.md 設定的有效性

預期成果

  • UserServiceTests.cs 檔案包含四個方法的完整測試
  • 所有測試程式碼符合專案規範並通過測試執行
  • Mock 物件設定正確,依賴互動驗證完整
  • 測試涵蓋成功路徑、驗證失敗、依賴異常、參數驗證等情境

技巧提示

  • 使用相同的三個通用指令範本(與階段一相同)
  • 對於純邏輯方法(ValidatePasswordIsValidEmail),GitHub Copilot 會自動產生無 Mock 的測試
  • 對於依賴注入方法(UpdateUserStatusAsync),會自動包含 Mock 物件設定
  • 注意測試方法命名和 DisplayName 的一致性
  • 驗證最終測試執行結果

準備好進入階段三了嗎?請確保已完成階段二的練習,接下來就讓我們繼續下去。

階段三:業務流程測試應用練習

在前兩階段中,我們學會了處理單一方法的測試。現在進入階段三,練習將這些技巧應用到業務流程測試場景。

學習重點

練習重點

  • 多步驟業務邏輯:練習測試包含多個執行步驟的方法
  • 異常處理測試:驗證不同失敗點的處理邏輯
  • Mock 協調應用:運用多個 Mock 物件的測試場景

實務應用價值

  • 練習測試包含多個步驟的業務方法
  • 熟悉處理業務流程中的異常情境

步驟 3-1:更新 copilot-instructions.md(加入業務流程測試規範)

階段三需要在現有的 Mock 框架規範基礎上,加入業務流程測試的特殊設定。

重要:階段三是在階段二的基礎上繼續添加內容,不是替換整個檔案。

.github/copilot-instructions.md 檔案末尾添加以下內容:

## 業務流程測試規範
- 使用 Received.InOrder() 驗證方法呼叫順序
- 針對業務流程的每個步驟設計失敗測試
- 驗證業務規則和狀態轉換邏輯
- 測試異常情況下的資料一致性
- 重點關注業務邏輯的完整性而非技術實作細節

## 複雜測試情境設計
- 正常業務流程:完整的步驟執行和結果驗證
- 業務規則驗證:狀態檢查、權限驗證、業務條件
- 異常處理測試:每個失敗點的獨立測試
- 邊界條件測試:業務邏輯的臨界值和特殊狀況
- 狀態一致性測試:確保資料狀態的正確轉換

階段三新增規範說明

  1. 順序驗證:使用 Received.InOrder() 確保業務步驟的執行順序
  2. 業務流程測試:關注業務邏輯的完整性
  3. 異常處理:系統性測試每個可能的失敗點
  4. 狀態管理:驗證業務狀態的正確轉換

步驟 3-2:學習業務流程分析指令

階段三繼續使用與前兩階段相同的通用指令範本,但分析重點會自動調整到業務流程的複雜性。

與前兩階段的一致性

  • 指令範本完全相同:確保學習一致性和可重複性
  • 分析重點自然升級:GitHub Copilot 會自動識別業務流程的複雜性
  • 測試情境自動豐富:會產生更多關於業務流程和狀態轉換的測試情境

實際操作:分析 OrderProcessor.CancelOrderAsync

在 GitHub Copilot Chat 中,切換到 Agent 模式,輸入以下指令:

請為 `src/Day27.Core/Services/OrderProcessor.cs` 中的 `CancelOrderAsync` 方法進行測試分析,並將結果儲存為 `OrderProcessor_TestScenario.md` 檔案:

**檔案標題格式**:# OrderProcessor 測試情境分析

**內容結構**(請嚴格按照以下編號和標題):

## CancelOrderAsync 方法

### 1. 方法簽名分析
列出參數類型、回傳類型、是否為非同步

### 2. 業務邏輯分析
識別主要的邏輯分支和驗證規則

### 3. 測試情境建議
涵蓋正常情境、邊界條件、異常處理的核心測試情境,每個情境包含具體輸入值、預期結果、驗證重點

### 4. 依賴關係識別
列出需要 Mock 的外部依賴(如有),如果是純函數請註明無外部依賴

**重要限制**:
- 只產生上述 4 個區段,不要增加其他內容
- 不要包含測試實作建議、測試覆蓋率目標等額外內容
- 依賴關係分析請專注於外部服務、資料庫、其他類別等,不要提及 .NET Framework 內建功能

請將完整分析結果儲存到 `docs/testing/OrderProcessor_TestScenario.md` 檔案中。

階段三的分析結果特點

使用相同的指令範本分析 OrderProcessor 的 CancelOrderAsync 方法,會產生以下特點的結果:

1. 方法簽名分析

  • 識別非同步方法特性
  • 分析參數和回傳類型的業務意義

2. 業務邏輯分析

  • 資料載入步驟:從資料庫獲取訂單
  • 業務規則驗證:檢查訂單狀態是否允許取消
  • 狀態轉換邏輯:將訂單狀態更新為已取消
  • 資料持久化:保存更新後的訂單

3. 測試情境建議

  • 正常情境:有效訂單的成功取消流程
  • 業務規則測試:已付款訂單、已取消訂單的拒絕邏輯
  • 資料驗證:不存在訂單的處理
  • 狀態一致性:確保狀態正確更新

4. 依賴關係識別

  • IOrderRepository:資料存取依賴,需要 Mock

步驟 3-3:業務流程測試情境設計

階段三的重點是如何設計測試不同失敗點和業務規則的情境。

實際操作:產生 OrderProcessor 測試程式碼

在 GitHub Copilot Chat 中,切換到 Agent 模式,輸入以下指令:

請在測試專案中建立 `OrderProcessorTests.cs` 檔案,並基於 `OrderProcessor_TestScenario.md` 中的測試情境產生完整的 xUnit 測試類別:

**重要**:請嚴格遵循專案的 copilot-instructions.md 規範

**測試情境來源**:請參考您的測試文件目錄中的 `OrderProcessor_TestScenario.md` 檔案中的 CancelOrderAsync 方法測試情境清單

**程式碼要求**:
- 3A Pattern 並標註註解
- DisplayName 屬性
- 依據測試情境產生對應的測試方法

GitHub Copilot 的進階自動化處理

由於 copilot-instructions.md 已包含業務流程測試規範,GitHub Copilot 會自動:

  1. 建立完整的 Mock 設定

    private readonly IOrderRepository mockOrderRepository;
    private readonly IPaymentService mockPaymentService;
    private readonly IInventoryService mockInventoryService;
    private readonly INotificationService mockNotificationService;
    private readonly OrderProcessor orderProcessor;
    
  2. 設定業務規則測試

    // Arrange
    var existingOrder = new Order { Id = 1, Status = OrderStatus.Paid };
    mockOrderRepository.GetByIdAsync(1).ReturnsAsync(existingOrder);
    
    // Act & Assert
    var exception = await Assert.ThrowsAsync<InvalidOperationException>(
        () => orderProcessor.CancelOrderAsync(1));
    exception.Message.Should().Be("已付款的訂單無法取消");
    
  3. 驗證狀態轉換

    // Assert
    result.Status.Should().Be(OrderStatus.Cancelled);
    await mockOrderRepository.Received(1).SaveAsync(Arg.Is<Order>(o => o.Status == OrderStatus.Cancelled));
    

步驟 3-4:進階測試程式碼產生

階段三的重點是處理業務規則驗證和狀態轉換的測試。

建置與測試驗證

產生測試程式碼後,進行建置和測試執行:

使用 GitHub Copilot Chat Agent 模式

請執行以下步驟來建置專案並執行測試:

1. 建置整個解決方案
2. 執行剛產生的 CancelOrderAsync 方法相關測試
3. 顯示測試結果摘要

請使用 FullyQualifiedName 篩選,只執行方法名稱包含 "CancelOrderAsync" 的測試方法。

如果有建置錯誤或測試失敗,請說明問題並提供修正建議。

直接使用終端機指令

# 建置專案
dotnet build

# 方式 1:使用方法名稱前綴篩選 CancelOrderAsync 相關測試
dotnet test --filter "FullyQualifiedName~CancelOrderAsync" --verbosity normal

# 方式 2:使用 DisplayName 前綴篩選相同功能的測試
dotnet test --filter "DisplayName~取消訂單" --verbosity normal

# 方式 3:使用 FullyQualifiedName 篩選特定測試類別
dotnet test --filter "FullyQualifiedName~OrderProcessorTests" --verbosity normal

步驟 3-5:業務流程測試驗證

篩選指令說明

  • 方式 1FullyQualifiedName~CancelOrderAsync 會執行所有方法名稱包含「CancelOrderAsync」的測試
  • 方式 2DisplayName~取消訂單 會執行所有 DisplayName 包含「取消訂單」的測試(推薦新方式)
  • 方式 3FullyQualifiedName~OrderProcessorTests 會執行整個 OrderProcessorTests 類別的所有測試

重要:階段三的驗證重點在於確保業務規則和狀態轉換邏輯的正確性。

階段三學習成果驗證

完成階段三後,你應該能夠:

驗證練習

OrderProcessor 還有一個更複雜的公開方法尚未建立測試:ProcessOrderAsync。請使用學到的指令範本,為這個方法產生完整的測試:

練習目標

  1. 完整的 OrderProcessor 測試覆蓋

    • 目前已有 CancelOrderAsync 的測試
    • 需要補齊 ProcessOrderAsync 方法的測試
  2. 多步驟業務流程測試

    • ProcessOrderAsync:包含 5 個連續步驟的複雜業務流程
    • 檢查庫存 → 保留庫存 → 處理付款 → 更新訂單 → 發送通知
    • 每個步驟都可能失敗,需要測試不同失敗點
  3. 階段三技巧整合驗證

    • 驗證業務流程測試規範的應用
    • 確認複雜依賴協調的測試設計
    • 測試順序驗證和異常處理策略

預期成果

  • OrderProcessor_TestScenario.md 檔案包含兩個方法的完整分析
  • OrderProcessorTests.cs 檔案包含兩個方法的完整測試
  • 所有測試程式碼符合階段三的業務流程測試規範
  • ProcessOrderAsync 的測試涵蓋:
    • 完整業務流程的成功路徑
    • 每個步驟失敗點的獨立測試
    • 依賴服務的順序驗證
    • 異常處理和清理邏輯驗證

技巧提示

  • 使用相同的三個通用指令範本(與階段一、二相同)
  • 重點關注業務流程:ProcessOrderAsync 包含 5 個連續步驟,是測試順序驗證的絕佳練習
  • 依賴協調複雜性:需要同時 Mock 4 個不同的依賴服務
  • 異常處理策略:測試在不同步驟失敗時的處理邏輯
  • 驗證 DisplayName 功能分組:測試「處理訂單」功能的篩選效果

掌握的技巧

  1. 設定業務流程測試環境

    • 更新 copilot-instructions.md 加入業務流程測試規範
    • 了解如何配置複雜業務邏輯的測試環境
  2. 3 個統一的測試指令範本

    • 業務邏輯分析指令(階段一、二、三通用)
    • 測試程式碼產生指令(階段一、二、三通用)
    • 建置與測試驗證(階段一、二、三通用)
  3. 標準化業務流程測試開發流程

    • 業務流程分析 → 狀態轉換設計 → 規則驗證測試 → 整合驗證
    • 處理複雜業務規則的測試技巧
    • 建立完整且可靠的業務邏輯測試

結語

經過三個階段的實驗,你應該對「如何讓 AI 幫助測試開發」有了一些具體的想法。上面所介紹的方法不是萬能的,但提供了一個思考方向:如何系統化地引導 AI 從業務邏輯分析開始,逐步產生有用的測試程式碼。

這次實驗的收穫

流程理解

  • 階段一:DiscountCalculator - 理解純邏輯測試的 AI 輔助方法
  • 階段二:UserService - 探索 Mock 物件處理的指令設計
  • 階段三:OrderProcessor - 練習多步驟業務流程的測試應用

方法框架

  • 3 個指令範本:業務邏輯分析、測試情境設計、程式碼產生的基本架構
  • copilot-instructions.md:如何透過設定檔引導 AI 產出符合規範的程式碼
  • 系統化流程:分析 → 設計 → 產生 → 驗證的標準化步驟

實驗心得

  • 有效但需要調整:這套方法確實能引導 AI 產生較好的測試程式碼,但每個專案都需要客製化
  • 思路比指令重要:重點不是照抄這些指令,而是理解如何系統化地與 AI 協作
  • 仍需人工驗證:AI 產生的內容品質有限,最終還是需要開發者審查和調整

後續思考方向

文章裡所介紹的這些方法的價值在於「給你一個方向」:

  1. 嘗試應用到你的專案

    • 參考這篇文章的 copilot-instructions.md,設計符合你團隊需求的版本
    • 從簡單的類別開始實驗,逐步調整指令範本
  2. 建立團隊共識

    • 與團隊討論是否要導入 AI 輔助測試開發
    • 制定團隊的測試程式碼標準和 AI 使用規範
  3. 持續迭代改進

    • 記錄哪些指令有效、哪些需要調整
    • 累積適合你專案特性的指令範本庫

最後提醒:這些方法只是一個起點,重點是讓你看看怎麼設計適合自己團隊的 AI 協作流程。每個專案的需求都不同,不要期待能直接套用,而是要根據實際情況持續調整。


參考資源

GitHub Copilot 官方文件

Microsoft Learn 學習資源

社群資源與設定

範例程式碼


這是「重啟挑戰:老派軟體工程師的測試修練」的第二十七天。明天會介紹 Day 28 – TUnit 入門:下世代 .NET 測試框架探索。


上一篇
Day 26 – xUnit 升級指南:從 2.9.x 到 3.x 的轉換
下一篇
Day 28 - TUnit 入門 - 下世代 .NET 測試框架探索
系列文
重啟挑戰:老派軟體工程師的測試修練31
圖片
  熱門推薦
圖片
{{ item.channelVendor }} | {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言