在 ASP.NET Core Blazor 中測試 Razor 元件

注意

這不是這篇文章的最新版本。 如需目前版本,請參閱本文的 .NET 8 版本

重要

這些發行前產品的相關資訊在產品正式發行前可能會有大幅修改。 Microsoft 對此處提供的資訊,不做任何明確或隱含的瑕疵擔保。

如需目前版本,請參閱本文的 .NET 8 版本

作者:Egil Hansen

測試 Razor 元件是發行穩定且可維護 Blazor 應用程式的重要層面。

若要測試 Razor 元件,受測元件 (CUT) 為:

  • 使用測試的相關輸入轉譯。
  • 視所執行的測試類型而定,可能受限於互動或修改。 例如,可以觸發事件處理常式,例如按鈕的 onclick 事件。
  • 檢查是否有預期的值。 當一或多個受檢的值符合測試的預期值時,測試就會通過。

測試方法

測試 Razor 元件的兩種常見方法為端對端 (E2E) 測試和單元測試:

  • 單元測試單元測試是以提供下列各項的單元測試程式庫撰寫:

    • 元件轉譯。
    • 檢查元件輸出和狀態。
    • 觸發事件處理常式和生命週期方法。
    • 元件行為正確的判斷提示。

    bUnit 是可啟用 Razor 元件單元測試的程式庫範例。

  • E2E 測試:測試執行器會執行包含 CUT 的 Blazor 應用程式,並將瀏覽器執行個體自動化。 測試工具會透過瀏覽器檢查並與 CUT 互動。 適用於 .NET 的 Playwright 是 E2E 測試架構的範例,可與 Blazor 應用程式搭配使用。

在單元測試中,只涉及 Razor 元件 (Razor/C#)。 必須模擬外部相依性,例如服務和 JS Interop。 在 E2E 測試中,Razor 元件及其所有輔助基礎結構都是測試的一部分,包括 CSS、JS、DOM 和瀏覽器 API。

「測試範圍」描述測試的範圍有多廣泛。 測試範圍通常會影響測試的速度。 單元測試會在應用程式的子系統子集上執行,且通常會以毫秒為單位執行。 E2E 測試會測試應用程式子系統的廣泛群組,可能需要幾秒鐘的時間才能完成。

單元測試也提供 CUT 執行個體的存取權,允許檢查和驗證元件的內部狀態。 這在 E2E 測試中通常不可能。

就元件的環境而言,E2E 測試必須確定在驗證開始之前,已達到預期的環境狀態。 否則,無法預測結果。 在單元測試中,CUT 轉譯和測試生命週期更加整合,可改善測試穩定性。

E2E 測試牽涉到啟動多個處理序、網路和磁碟 I/O,以及通常會導致測試可靠性不佳的其他子系統活動。 單元測試通常可避免這幾種問題。

下表摘要說明這兩種測試方法之間的差異。

功能 單元測試 E2E 測試
測試範圍 僅限 Razor 元件 (Razor/C#) 搭配 CSS/JS 的 Razor 元件 (Razor/C#)
測試執行時間 毫秒
存取元件執行個體 No
對環境敏感 No Yes
可靠性 更加可靠 較不可靠

選擇最適當的測試方法

選擇要執行的測試類型時,請考量情節。 下表描述一些考量。

案例 建議的方法 備註
不含 JS Interop 邏輯的元件 單元測試 當 Razor 元件中沒有 JS Interop 的相依性時,不需存取 JS 或 DOM API 即可測試該元件。 在此情節中,選擇單元測試沒有任何缺點。
具有簡單 JS Interop 邏輯的元件 單元測試 元件通常會透過 JS Interop 查詢 DOM 或觸發動畫。 在此情節中,通常慣用單元測試,因為透過 IJSRuntime 介面模擬 JS 互動相當簡單。
相依於複雜 JS 程式碼的元件 單元測試和個別 JS 測試 如果元件使用 JS Interop 呼叫大型或複雜的 JS 程式庫,但 Razor 元件與 JS 程式庫之間的互動很簡單,則最佳方法可能是將元件和 JS 程式庫或程式碼視為兩個不同的部分並各自分開測試。 使用單元測試程式庫測試 Razor 元件,並使用 JS 測試程式庫測試 JS。
具有的邏輯取決於瀏覽器 DOM 的 JS 操作的元件 E2E 測試 當元件的功能相依於 JS 及其 DOM 操作時,請在 E2E 測試中一起確認 JS 和 Blazor 程式碼。 這是 Blazor 架構開發人員搭配 Blazor 瀏覽器轉譯邏輯採用的方法,其具有緊密結合的 C# 和 JS 程式碼。 C# 和 JS 程式碼必須一起運作,才能在瀏覽器中正確轉譯 Razor 元件。
相依於具有難以模擬相依性的第三方類別庫的元件 E2E 測試 當元件的功能相依於具有難以模擬相依性的第三方類別庫 (例如 JS Interop) 時,E2E 測試可能是測試元件的唯一選項。

使用 bUnit 測試元件

Blazor 沒有官方的 Microsoft 測試架構,但社群驅動的專案 bUnit 提供便利的方法來單元測試 Razor 元件。

注意

bUnit 是第三方測試程式庫,Microsoft 不提供支援或維護。

bUnit 可搭配一般用途的測試架構運作,例如 MSTestNUnitxUnit。 這些測試架構讓 bUnit 測試的外觀和操作如同一般單元測試。 與一般用途測試架構整合的 bUnit 測試通常使用以下項目執行:

注意

不同測試架構的測試概念和測試實作彼此相似但並不相同。 如需指引,請參閱測試架構的文件。

以下示範在以 Blazor 專案範本為基礎的應用程式中,Counter 元件上的 bUnit 測試結構。 Counter 元件會根據在頁面中選取按鈕的使用者來顯示和遞增計數器:

@page "/counter"

<h1>Counter</h1>

<p>Current count: @currentCount</p>

<button class="btn btn-primary" @onclick="IncrementCount">Click me</button>

@code {
    private int currentCount = 0;

    private void IncrementCount()
    {
        currentCount++;
    }
}

下列 bUnit 測試會驗證當已選取按鈕時,CUT 的計數器是否正確遞增:

@code {
    [Fact]
    public void CounterShouldIncrementWhenClicked()
    {
        // Arrange
        using var ctx = new TestContext();
        var cut = ctx.Render(@<Counter />);
        var paraElm = cut.Find("p");

        // Act
        cut.Find("button").Click();

        // Assert
        var paraElmText = paraElm.TextContent;
        paraElm.MarkupMatches("Current count: 1");
    }
}

測試也可以寫入 C# 類別檔案中:

public class CounterTests
{
    [Fact]
    public void CounterShouldIncrementWhenClicked()
    {
        // Arrange
        using var ctx = new TestContext();
        var cut = ctx.RenderComponent<Counter>();
        var paraElm = cut.Find("p");

        // Act
        cut.Find("button").Click();

        // Assert
        var paraElmText = paraElm.TextContent;
        paraElmText.MarkupMatches("Current count: 1");
    }
}

下列動作會在測試的每個步驟進行:

  • ArrangeCounter 元件會使用 bUnit 的 TestContext 來轉譯。 CUT 的段落元素 (<p>) 已找到並指派給 paraElm。 在 Razor 語法中,元件可以當做 RenderFragment 傳遞至 bUnit。

  • Act:按鈕的元素 (<button>) 是藉由呼叫 Click 來尋找並選取,該元素應使計數器遞增並更新段落標籤 (<p>) 的內容。 段落元素文字內容可藉由呼叫 TextContent 取得。

  • Assert:在文字內容上呼叫 MarkupMatches,以確認其是否符合預期的字串,也就是 Current count: 1

注意

MarkupMatches 判斷提示與一般字串比較判斷提示不同 (例如 Assert.Equal("Current count: 1", paraElmText);)。 MarkupMatches 會執行輸入和預期 HTML 標記的語意比較。 語意比較會感知 HTML 語意,這表示忽略微不足道的空白字元等事物。 這可造就更穩定的測試。 如需詳細資訊,請參閱自訂語意 HTML 比較

其他資源