2025年12月30日 星期二

使用GitHub Copilot協助撰寫Blazor Server應用程式 - 2

 


本文將延續本站文章《使用GitHub Copilot協助撰寫Blazor Server應用程式 - 1》一文的情境,介紹如何在Visual Studio Code開發工具使用GitHub Copilot與其進階功能代理模式(Agent Mode) 來開發 ASP.NET Core Blazor Server 專案,讓開發者能以自然語言與 AI 對話,從專案建立、元件設計、Web API 撰寫到單元測試與除錯,全程自動化完成,展現出生成式 AI輔助設計的強大潛力與高效率。

在上一篇文章中,我們使用GitHub Copilot 代理模式(Agent Mode)設計模型(Models)與Web API控制器,接下來就來談談如何撰寫測試程式碼、設計Blazor元件以及發生錯誤時,如何引導AI協助解決錯誤。


撰寫測試程式碼

首先讓我們要求GitHub Copilot 代理模式(Agent Mode)來幫助撰寫「EmployeesController」類別的單元測試程式碼。目前Web API控制器的程式碼如下列:

EmployeesController.cs

using Microsoft.AspNetCore.Mvc;

using BlazorDemo.Models;

 

namespace BlazorDemo.Controllers

{

    /// <summary>

    /// 員工資料 API 控制器

    /// </summary>

    [ApiController]

    [Route( "api/[controller]" )]

    public class EmployeesController : ControllerBase

    {

        private static readonly List<Employee> _employees = new()

        {

            new Employee

            {

                Id = 1,

                Name = "張小明",

                Email = "xiaoming.zhang@example.com"

            },

            new Employee

            {

                Id = 2,

                Name = "李美玲",

                Email = "meiling.li@example.com"

            },

            new Employee

            {

                Id = 3,

                Name = "王大同",

                Email = "datong.wang@example.com"

            }

        };

 

        /// <summary>

        /// 取得所有員工資料

        /// </summary>

        /// <returns>員工資料清單</returns>

        [HttpGet]

        public ActionResult<IEnumerable<Employee>> Get()

        {

            return Ok( _employees );

        }

    }

}


Visual Studio Code開發工具Chat視窗加入提示如下:

使用 xUnit 撰寫測試,驗證錯誤處理是否正確運作

為每項變更撰寫文件。


Chat視窗使用「Add Context」功能加入「EmployeesController」類別,然後執行提示:

1:撰寫測試程式碼


執行的結果請參考下圖所示:

2:撰寫測試程式碼執行的結果

 

GitHub Copilot 代理模式(Agent Mode)也會自動跑完單元測試,測試結果參考如下:

3:單元測試的測試結果



檢視Visual Studio Code工具檔案總管視窗,可以看到GitHub Copilot代理模式(Agent Mode)產生了一個新的測試專案與測試程式碼,請參考下圖所示:

4GitHub Copilot代理模式(Agent Mode)產生了新的測試專案與測試程式碼

 

產生的「EmployeesControllerTests.cs」程式如下所列:

EmployeesControllerTests.cs

using System.Collections.Generic;

using BlazorDemo.Controllers;

using BlazorDemo.Models;

using Microsoft.AspNetCore.Mvc;

using Xunit;

 

namespace BlazorDemo.Tests

{

    /// <summary>

    /// EmployeesController 單元測試

    /// </summary>

    public class EmployeesControllerTests

    {

        private readonly EmployeesController _controller;

 

        public EmployeesControllerTests()

        {

            _controller = new EmployeesController();

        }

 

        [Fact( DisplayName = "Get 方法應回傳所有員工資料" )]

        public void Get_ReturnsAllEmployees()

        {

            // Arrange & Act

            var result = _controller.Get();

 

            // Assert

            var okResult = Assert.IsType<OkObjectResult>( result.Result );

            var employees = Assert.IsAssignableFrom<IEnumerable<Employee>>( okResult.Value );

            Assert.NotEmpty( employees );

        }

 

        [Fact(DisplayName = "確認回傳的員工資料內容正確")]

        public void Get_ReturnsCorrectEmployeeData()

        {

            // Arrange & Act

            var result = _controller.Get();

           

            // Assert

            var okResult = Assert.IsType<OkObjectResult>( result.Result );

            var employees = Assert.IsAssignableFrom<IEnumerable<Employee>>( okResult.Value );

            var employeeList = employees.ToList();

           

            Assert.Equal( 3, employeeList.Count );

            Assert.Contains( employeeList, e => e.Id == 1 && e.Name == "張小明" );

            Assert.Contains( employeeList, e => e.Id == 2 && e.Name == "李美玲" );

            Assert.Contains( employeeList, e => e.Id == 3 && e.Name == "王大同" );

        }

 

        [Fact(DisplayName = "確認回傳的狀態碼為 200 OK")]

        public void Get_ReturnsOkStatusCode()

        {

            // Arrange & Act

            var result = _controller.Get();

           

            // Assert

            var okResult = Assert.IsType<OkObjectResult>( result.Result );

            Assert.Equal( 200, okResult.StatusCode );

        }

    }

}

 

撰寫單元測試報告文件

GitHub Copilot 代理模式(Agent Mode)不僅可以幫您撰寫單元測試程式碼,也可以幫助撰寫單元測試報告文件,在Chat視窗加入提示如下:

將剛剛的測試結果的技術文件輸出成一份TestResult.md檔,Markdown格式,放在專案根目錄


執行的結果請參考下圖所示:

5:撰寫單元測試報告文件



產生的「TestResult.md」檔案內容參考如下:

TestResult.md

# EmployeesController 單元測試報告

 

## 測試執行日期

2025-11-06

 

## 概述

此測試套件針對 `EmployeesController` 進行單元測試,確保其基本功能和錯誤處理機制正常運作。測試使用 xUnit 框架執行,涵蓋了主要的 API 功能和回應驗證。

 

## 測試案例詳細說明

 

### 1. Get_ReturnsAllEmployees

**目的**:驗證 GET 方法是否能正確返回所有員工資料

 

**驗證項目**

- 回傳型別為 `OkObjectResult`

- 資料集合不為空

- 回傳型別符合 `IEnumerable<Employee>`

 

### 2. Get_ReturnsCorrectEmployeeData

**目的**:驗證回傳的員工資料內容正確性

 

**驗證項目**

- 資料筆數為 3

- 每筆資料的 Id Name 符合預期

- 確認特定員工資料存在

  - 張小明 (ID: 1)

  - 李美玲 (ID: 2)

  - 王大同 (ID: 3)

 

### 3. Get_ReturnsOkStatusCode

**目的**:驗證 API 回傳的 HTTP 狀態碼

 

**驗證項目**

- 確認回傳狀態碼為 200 (OK)

- 驗證回傳結果型別

 

## 測試環境設置

- **測試框架**xUnit

- **初始化方式**:建構函式中初始化控制器實例

- **資料處理**:使用靜態資料,無需 Mock 物件

 

## 建議的後續改進

 

### 優先度高

1. 新增錯誤處理的測試案例

   - 無效的請求參數

   - 資料驗證失敗情況

   - 例外狀況處理

 

### 優先度中

2. 加入更多邊界條件測試

   - 極限值測試

   - 特殊字元處理

   - 空值處理

 

### 優先度低

3. 考慮新增以下測試案例

   - 資料驗證測試

   - 非同步操作測試

   - 效能測試

 

## 結論

所有測試案例均已通過,確保了 API 的基本功能正常運作。測試覆蓋了主要的使用場景,提供了程式碼品質的基本保障。建議根據後續改進建議持續擴充測試案例,以提高程式碼的穩定性和可靠性。

 

設計Blazor元件

最後我們來讓GitHub Copilot代理模式(Agent Mode)設計Blazor元件當成使用者操作介面。在Chat視窗加入提示如下:

加入一個EmployeeList元件,叫用EmployeesController,取得所有Employee資料使用表格顯示之。

在選單加入一個連結以載入EmployeeList元件


執行的結果請參考下圖所示:

6:設計Blazor元件

 
錯誤處理

在與GitHub Copilot 代理模式(Agent Mode)互動的過程中當然不會每次都順利,有可能產生出有問題的程式碼,若執行出錯該如何處理呢?例如筆者在設計Blazor元件的過程中發生一個問題,當網站程式執行後,元件無法載入並顯示員工清單,一直停留在「載入中」的畫面,請參考下圖所示:

7:執行錯誤畫面


我們可以讓GitHub Copilot 代理模式(Agent Mode)幫忙除錯,在Chat視窗中點選「Attach Context」按鈕加入「Problems」項目,請參考下圖所示:

8:加入「Programs」項目協助除錯



然後繼續加入懷疑有出問題的程式檔案,例如「EmployeeList.razor」;加入上圖執行出錯的截圖畫面,然後加入「/fix」斜線命令,撰寫提示如下:

/fix 執行後EmployeeList沒有顯示員工清單


請參考下圖所示:

9:修訂程式問題



GitHub Copilot修定後的執行的結果請參考下圖所示:

10:修訂程式問題


經過幾回合的溝通,現在Blazor元件便能正常運作了,執行的結果請參考下圖所示:

11Blazor元件執行結果



最終產生的「EmployeeList.razor」元件程式碼參考如下:

EmployeeList.razor

@page "/employees"

@using BlazorDemo.Models

@inject HttpClient Http

 

<HeadContent>

    <title> 員工清單 </title>

</HeadContent>

 

<h1>員工清單</h1>

 

@if (employees == null)

{

    <p> <em> 載入中... </em> </p>

}

else

{

    <table class = "table">

        <thead>

            <tr>

                <th> 編號 </th>

                <th> 姓名 </th>

                <th> Email </th>

            </tr>

        </thead>

        <tbody>

            @foreach ( var employee in employees )

            {

                <tr>

                    <td> @employee.Id </td>

                    <td> @employee.Name </td>

                    <td> @employee.Email </td>

                </tr>

            }

        </tbody>

    </table>

}

 

@code {

    private Employee[]? employees;

 

    protected override async Task OnInitializedAsync()

    {

        try

        {

            employees = await Http.GetFromJsonAsync<Employee[]>( "api/employees" );

        }

        catch (Exception ex)

        {

            Console.WriteLine( $"Error loading employees: {ex.Message}" );

            // 顯示錯誤訊息在控制台,方便除錯

            System.Diagnostics.Debug.WriteLine( $"Error loading employees: {ex.Message}" );

        }

    }

}



GitHub Copilot在導覽選單「NavMenu.razor」程式檔案中加入一個「員工清單」連結:

NavMenu.razor

<div class = "top-row ps-3 navbar navbar-dark">

    <div class = "container-fluid">

        <a class = "navbar-brand" href = ""> BlazorDemo </a>

    </div>

</div>

 

<input type = "checkbox" title = "Navigation menu" class = "navbar-toggler" />

 

<div class = "nav-scrollable" onclick = "document.querySelector('.navbar-toggler').click()">

    <nav class = "nav flex-column">

        <div class = "nav-item px-3">

            <NavLink class = "nav-link" href = "" Match = "NavLinkMatch.All">

                <span class = "bi bi-house-door-fill-nav-menu" aria-hidden = "true"> </span> 首頁

            </NavLink>

        </div>

 

        <div class = "nav-item px-3">

            <NavLink class = "nav-link" href = "privacy">

                <span class = "bi bi-shield-fill-nav-menu" aria-hidden = "true"> </span> 版權宣告

            </NavLink>

        </div>

 

        <div class = "nav-item px-3">

            <NavLink class = "nav-link" href = "employees">

                <span class = "bi bi-people-fill-nav-menu" aria-hidden = "true"> </span> 員工清單

            </NavLink>

        </div>

    </nav>

</div>

 


最終產生的「
Program.cs」檔案的程式碼參考如下,自動註冊「HttpClient」服務讓Blazor透過它叫用Web API取得員工清單來顯示:

Program.cs

using BlazorDemo.Components;

 

var builder = WebApplication.CreateBuilder(args);

 

// Add services to the container.

builder.Services.AddRazorComponents()

    .AddInteractiveServerComponents();

 

// 加入 HttpClient 服務

builder.Services.AddScoped( sp => new HttpClient

{

    BaseAddress = new Uri("http://localhost:5086")

});

 

// 加入 Web API 服務

builder.Services.AddControllers();

builder.Services.AddEndpointsApiExplorer();

 

var app = builder.Build();

 

// Configure the HTTP request pipeline.

if (!app.Environment.IsDevelopment())

{

    app.UseExceptionHandler( "/Error", createScopeForErrors: true );

    // The default HSTS value is 30 days. You may want to change this for production scenarios, see https://aka.ms/aspnetcore-hsts.

    app.UseHsts();

}

 

app.UseHttpsRedirection();

 

app.UseAntiforgery();

 

app.MapStaticAssets();

app.MapRazorComponents<App>()

    .AddInteractiveServerRenderMode();

 

// 加入 Web API 路由映射

app.MapControllers();

 

app.Run();

 

工作流程圖

最後,對於複雜的專案我們可以請GitHub Copilot 代理模式(Agent Mode)繪製圖表來說明,在Chat視窗加入提示如下:

建立應用程式的工作流程圖

 

執行的結果請參考下圖所示:

12:產生工作流程圖



參考產生的「Workflow.md」檔案內容如下:

Workflow.md

# Blazor Server 應用程式工作流程圖

 

以下以繁體中文說明 Blazor Server 應用程式的主要執行流程,並提供一個 Mermaid 工作流程圖,說明從啟動到 UI 顯示、資料取得與靜態資源的處理路徑。

 

## 快速說明

- 平台:ASP.NET Core 9.0 / Blazor Server

- 主要檔案:`Program.cs`(啟動、服務註冊)、`Components/Pages/*.razor`(頁面元件)、`Controllers/EmployeesController.cs`API 控制器)、`Models/Employee.cs`(資料模型)、`wwwroot/`(靜態資源)

- 通訊:Blazor Server 使用 SignalR 維持 client server circuit;頁面內若使用 HttpClient 呼叫 API,則透過標準 HTTP pipeline

 

## Mermaid 工作流程圖

 

```mermaid

flowchart TD

  A[啟動: Program.cs] --> B[服務註冊 (DI, HttpClient, DbContext...)]

  B --> C[建立 HTTP Pipeline (Routing, Endpoints, Static Files)]

  C --> D[靜態資源: wwwroot -> 瀏覽器]

 

  subgraph Blazor_Server

    E[瀏覽器連線 (SignalR)] --> F[建立 Circuit]

    F --> G[Render Root Component (App.razor)]

    G --> H[Layout (MainLayout, NavMenu)]

    H --> I[Page 元件 (例如: EmployeeList.razor)]

    I --> J{是否需呼叫 API}

    J -- --> K[HttpClient 呼叫 /api/employees]

    K --> L[EmployeesController](controller)

    L --> M[Business / Data (Models/Employee DB)]

    M --> N[回傳 JSON 結果]

    N --> I

    J -- --> O[直接在元件內使用 injected service state]

  end

 

  %% 顯示資料流程回到 UI

  N --> P[元件接收資料並 Render/re-render]

 

  %% 例外與錯誤處理

  L --> Q[錯誤處理 (返回 4xx/5xx)]

  Q --> I

 

  style Blazor_Server fill:#f9f,stroke:#333,stroke-width:1px

```

 

## 流程文字版(步驟)

1. 程式啟動:`Program.cs` 設定服務 (dependency injection)、註冊 `HttpClient`、資料庫上下文或其他服務。

2. 建立 HTTP pipelinemiddleware(靜態檔案、路由、端點)。

3. 使用者透過瀏覽器開啟網站,瀏覽器與 Server 建立 SignalR 連線(Blazor Server),建立一個 Circuit

4. Server Circuit 內渲染 Root component (`App.razor`),載入 `MainLayout` `NavMenu`

5. 使用者導航到頁面(例如 `/employees`),相對應的 `.razor` 元件被渲染(`EmployeeList.razor`)。

6. 若元件需要外部資料:它會透過注入的 `HttpClient` 呼叫 API(例如 `EmployeesController`),或直接注入一個資料服務。

7. API Controller (`EmployeesController`) 讀取 `Models/Employee` 或資料庫並回傳 JSON 給元件。

8. 元件接收資料並更新 UIBlazor 將觸發 re-render),同時維持 SignalR circuit 的連線狀態。

9. 靜態資源(CSS/JS)由 `wwwroot` 提供給瀏覽器;開發環境常用 `dotnet run` 啟動並可在本機檢視。

 

## 範例:EmployeeList 資料流(簡化)

- 使用者 -> 瀏覽器 -> Server (SignalR)

- EmployeeList.razor 呼叫 HttpClient -> /api/employees

- EmployeesController 拿到資料 -> 回傳 JSON

- EmployeeList 收到 JSON -> Render 表格

 

## 檔案與檢視建議

- 檔案位置:`BlazorDemo/Docs/Workflow.md`(本檔)

- VS Code 中可直接打開並用 Markdown Preview (Ctrl+Shift+V) 查看 Mermaid 圖(需安裝支援 Mermaid 的擴充套件,如 "Markdown Preview Mermaid Support")。

- 若要在專案內直接產生 SVG,建議使用外部工具或 VS Code 擴充功能匯出。

 

## 下一步建議

- 若需要,我可以把這張圖匯出為 SVG 檔並新增到 `wwwroot/docs/` 以便於瀏覽與列印。

- 我也能把流程轉成順序圖或更詳細的資料流圖(帶類別或端點呼叫),或產生簡短的 README 摘要。

 

---

 

檔案由自動化工具建立。如需調整內容或增加更細的流程圖(例如 DB schemaAuth 流程、SignalR 詳細時序),請告訴我想要的細節。

 

其中有使用Mermaid描述的流程圖。在Chat視窗加入提示如下,將繪製的Mermaid流程圖轉成SVG格式:

Mermaid 匯出成 SVG 並放到根目錄 ,檔名:workflow.svg


然後您就有詳細的專案運作流程圖可以參考了,執行的結果請參考下圖所示:

13Mermaid流程圖轉成SVG格式


總結

GitHub Copilot 價值所在不只是程式碼製造機,而是最佳實務加速器。本系列文章把「從零到可運作」全開發流程用 GitHub Copilot 串接在一起,說明如何讓 GitHub Copilot根據提示自動建立完整的專案架構,包括建立「Pages」、「Models」等資料夾與基本程式檔案,讓開發者能在短時間內啟動專案。接著透過「代理模式(Agent Mode)」來理解程式我們的需求,協助完成多步驟任務,甚至可自動修正錯誤、生成測試程式與技術文件,讓開發過程更為智慧化與精準。此外,這一系列文章也說明了 GitHub Copilot 如何透過「Inline Chat」與 Add Context」功能,根據上下文自動生成模型類別與 Web API 控制器程式碼,並能即時回應使用者的修改提示。最後應用程式碼重構,並把 Web APIxUnit 測試、測試報告、Mermaid 圖表補齊,讓專案具備可驗證、可協作與可維護性。


0 意見:

張貼留言