Skip to content
dotnet-testing-advanced-xunit-upgrade-guide logo

xUnit 升級指南:從 2.9.x 到 3.x

dotnet-testing-advanced-xunit-upgrade-guide

xUnit 2.9.x 到 3.x 升級完整指南。涵蓋破壞性變更、套件更新、async void 修正、IAsyncLifetime 調整。包含新功能介紹: Assert.Skip、Explicit Tests、Matrix Theory、Assembly Fixtures。 Keywords: xunit upgrade, xunit v3, xunit 3.x, xunit migration, xunit 升級, xunit.v3, OutputType Exe, IAsyncLifetime v3, Assert.Skip, SkipUnless, SkipWhen, Expli...

SKILL.md

Full skill instructions

xUnit 升級指南:從 2.9.x 到 3.x

適用情境

當被要求執行以下任務時,請使用此技能:

  • 將現有 xUnit 2.x 測試專案升級到 xUnit 3.x
  • 評估 xUnit 升級的影響範圍
  • 解決 xUnit 升級過程中的編譯錯誤
  • 使用 xUnit 3.x 新功能改進測試

核心概念

套件命名變革

xUnit v3 採用全新的套件命名策略:

v1~v2 套件名稱v3 套件名稱說明
xunitxunit.v3主要測試框架
xunit.assertxunit.v3.assert斷言函式庫
xunit.corexunit.v3.core核心元件
xunit.abstractions(移除)不再需要
xunit.runner.visualstudioxunit.runner.visualstudio (3.x.y)測試執行器

重要:使用 xunit.v3 套件名稱,不是 xunit。

最低運行時需求

xUnit 3.x 的嚴格要求:

  • .NET Framework 4.7.2+ 或
  • .NET 8.0+ (推薦)

不支援的版本:

  • .NET Core 3.1
  • .NET 5、6、7

破壞性變更清單

1. 測試專案變成可執行檔

<!-- xUnit 2.x (Library) -->
<PropertyGroup>
  <OutputType>Library</​OutputType>
</​PropertyGroup>

<!-- xUnit 3.x (Exe) - 必須變更 -->
<PropertyGroup>
  <OutputType>Exe</​OutputType>
</​PropertyGroup>

2. async void 測試不再支援

// ❌ xUnit 2.x - 3.x 中會失敗
[Fact]
public async void 測試某個非同步功能()
{
    var result = await SomeAsyncMethod();
    Assert.True(result);
}

// ✅ xUnit 3.x - 正確寫法
[Fact]
public async Task 測試某個非同步功能()
{
    var result = await SomeAsyncMethod();
    Assert.True(result);
}

3. IAsyncLifetime 變更

在 xUnit 3.x 中,IAsyncLifetime 繼承 IAsyncDisposable。如果同時實作 IAsyncLifetime 和 IDisposable,只會呼叫 DisposeAsync,不會呼叫 Dispose。

// ⚠️ 需要注意的模式
public class MyTestClass : IAsyncLifetime, IDisposable
{
    public async Task InitializeAsync() { /​* ... */ }
    public async Task DisposeAsync() { /​* 會被呼叫 */ }
    public void Dispose() { /​* 在 3.x 中不會被呼叫 */ }
}

// ✅ 建議:將清理邏輯統一放在 DisposeAsync
public class MyTestClass : IAsyncLifetime
{
    public async Task InitializeAsync() { /​* 初始化 */ }
    public async Task DisposeAsync() { /​* 所有清理邏輯 */ }
}

4. SkippableFact/​SkippableTheory 移除

// ❌ xUnit 2.x - 已移除
[SkippableFact]
public void 可跳過的測試()
{
    Skip.If(某個條件, "跳過原因");
    // 測試邏輯
}

// ✅ xUnit 3.x - 使用 Assert.Skip
[Fact]
public void 可跳過的測試()
{
    if (某個條件)
    {
        Assert.Skip("跳過原因");
    }
    // 測試邏輯
}

5. 僅支援 SDK-style 專案

檢查專案檔案開頭是否為:

<Project Sdk="Microsoft.NET.Sdk">

如果是傳統格式,必須先轉換為 SDK-style。


升級步驟

步驟 1:建立升級分支

git checkout -b feature/​upgrade-xunit-v3

步驟 2:更新專案檔案

<Project Sdk="Microsoft.NET.Sdk">
  <PropertyGroup>
    <TargetFramework>net8.0</​TargetFramework>
    <OutputType>Exe</​OutputType>
    <ImplicitUsings>enable</​ImplicitUsings>
    <Nullable>enable</​Nullable>
    <IsPackable>false</​IsPackable>
    <IsTestProject>true</​IsTestProject>
  </​PropertyGroup>

  <ItemGroup>
    <!-- xUnit v3 套件 -->
    <PackageReference Include="xunit.v3" Version="3.0.1" />
    <PackageReference Include="xunit.runner.visualstudio" Version="3.1.4">
      <IncludeAssets>runtime; build; native; contentfiles; analyzers</​IncludeAssets>
      <PrivateAssets>all</​PrivateAssets>
    </​PackageReference>
    <PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.13.0" />
    
    <!-- 常用輔助套件 -->
    <PackageReference Include="AwesomeAssertions" Version="8.1.0" />
    <PackageReference Include="NSubstitute" Version="5.3.0" />
  </​ItemGroup>
</​Project>

步驟 3:修正 async void 測試

使用 IDE 搜尋:

async\s+void.*\[(Fact|Theory)\]

將所有 async void 改為 async Task。

步驟 4:更新 using 陳述式

// 移除 (不再需要)
// using Xunit.Abstractions;

// 保留
using Xunit;

步驟 5:編譯與測試

dotnet clean
dotnet restore
dotnet build
dotnet test --verbosity normal

xUnit 3.x 新功能

動態跳過測試

聲明式 (SkipUnless/​SkipWhen):

[Fact(SkipUnless = nameof(IsWindowsEnvironment), 
      Skip = "此測試只在 Windows 環境執行")]
public void 只在Windows上執行的測試()
{
    // 測試邏輯
}

public static bool IsWindowsEnvironment => 
    RuntimeInformation.IsOSPlatform(OSPlatform.Windows);

命令式 (Assert.Skip):

[Fact]
public void 根據環境變數跳過的測試()
{
    var enableTests = Environment.GetEnvironmentVariable("ENABLE_INTEGRATION_TESTS");
    
    if (string.IsNullOrEmpty(enableTests) || enableTests.ToLower() != "true")
    {
        Assert.Skip("整合測試已停用。設定 ENABLE_INTEGRATION_TESTS=true 來執行");
    }
    
    // 測試邏輯...
}

明確測試 (Explicit Tests)

[Fact(Explicit = true)]
public void 昂貴的整合測試()
{
    // 這個測試預設不會執行,除非明確要求
    // 適用於效能測試、長時間執行的測試
}

[Test] 屬性

// 三種寫法功能相同
[Test]
public void 使用Test屬性的測試() { Assert.True(true); }

[Fact] 
public void 使用Fact屬性的測試() { Assert.True(true); }

矩陣理論資料 (Matrix Theory Data)

public static TheoryData<int, string> TestData =>
    new MatrixTheoryData<int, string>(
        [1, 2, 3],                    // 數字資料
        ["Hello", "World", "Test"]    // 字串資料
    );
    // 這會產生 3×3=9 個測試案例

[Theory]
[MemberData(nameof(TestData))]
public void 矩陣測試範例(int number, string text)
{
    number.Should().BePositive();
    text.Should().NotBeNullOrEmpty();
}

Assembly Fixtures

public class DatabaseAssemblyFixture : IAsyncLifetime
{
    public string ConnectionString { get; private set; }
    
    public async Task InitializeAsync()
    {
        // 建立測試資料庫
        ConnectionString = await CreateTestDatabaseAsync();
    }
    
    public async Task DisposeAsync()
    {
        // 清理測試資料庫
        await DropTestDatabaseAsync();
    }
}

// 註冊 Assembly Fixture
[assembly: AssemblyFixture(typeof(DatabaseAssemblyFixture))]

// 在測試中使用
public class UserServiceTests
{
    private readonly DatabaseAssemblyFixture _dbFixture;
    
    public UserServiceTests(DatabaseAssemblyFixture dbFixture)
    {
        _dbFixture = dbFixture;
    }
    
    [Fact]
    public void Test1() { /​* 使用 _dbFixture.ConnectionString */ }
}

Test Pipeline Startup

public class TestPipelineStartup : ITestPipelineStartup
{
    public async Task ConfigureAsync(ITestPipelineBuilder builder, 
                                     CancellationToken cancellationToken)
    {
        // 全域初始化邏輯
        Console.WriteLine("初始化測試環境...");
        await InitializeDatabaseAsync();
    }
}

// 註冊
[assembly: TestPipelineStartup(typeof(TestPipelineStartup))]

xunit.runner.json 設定

{
  "$schema": "https://xunit.net/​schema/​v3/​xunit.runner.schema.json",
  "parallelAlgorithm": "conservative",
  "maxParallelThreads": 4,
  "diagnosticMessages": true,
  "internalDiagnosticMessages": false,
  "methodDisplay": "classAndMethod",
  "preEnumerateTheories": true,
  "stopOnFail": false
}

測試報告格式

xUnit 3.x 支援多種報告格式:

# 產生 CTRF 格式報告
dotnet run -- -ctrf results.json

# 產生 TRX 格式報告  
dotnet run -- -trx results.trx

# 產生 XML 格式報告
dotnet run -- -xml results.xml

# 產生多種格式報告
dotnet run -- -xml results.xml -ctrf results.json -trx results.trx

常見問題與解決方案

問題 1:找不到 xunit.abstractions

錯誤:The type or namespace name 'Abstractions' does not exist

解決:移除 using Xunit.Abstractions;,相關類型已移到 Xunit 命名空間。

問題 2:自訂 DataAttribute 無法運作

// ❌ xUnit 2.x 的實作
public class CustomDataAttribute : DataAttribute
{
    public override IEnumerable<object[]> GetData(MethodInfo testMethod)
    {
        // 舊的實作
    }
}

// ✅ xUnit 3.x 的實作
public class CustomDataAttribute : DataAttribute
{
    public override async Task<IReadOnlyCollection<ITheoryDataRow>> GetDataAsync(
        MethodInfo method, 
        DisposalTracker disposalTracker)
    {
        var data = await GenerateDataAsync();
        return data.Select(item => new TheoryDataRow(item)).ToList();
    }
}

問題 3:IDE 無法發現測試

確認 IDE 版本符合要求:

  • Visual Studio 2022 17.8+
  • Rider 2023.3+
  • VS Code (最新版)

如仍有問題,可暫時停用 Microsoft Testing Platform:

<PropertyGroup>
  <EnableMicrosoftTestingPlatform>false</​EnableMicrosoftTestingPlatform>
</​PropertyGroup>

升級檢查清單

升級前

  • 確認目標框架版本 (.NET 8+ 或 .NET Framework 4.7.2+)
  • 檢查專案檔案格式 (SDK-style)
  • 識別所有 async void 測試方法
  • 檢查 IAsyncLifetime 實作
  • 評估相依套件相容性
  • 建立備份分支

升級過程

  • 更新套件參考 (使用 xunit.v3)
  • 移除 xunit.abstractions 參考
  • 修改 OutputType 為 Exe
  • 修正所有 async void 測試方法
  • 更新 using 陳述式
  • 重構自訂屬性 (如有)
  • 驗證編譯成功
  • 執行所有測試

升級後驗證

  • 功能完整性測試
  • 效能基準比較
  • CI/​CD Pipeline 驗證
  • 文檔更新
  • 團隊培訓

IDE 與工具支援

IDE 版本需求

IDE最低版本
Visual Studio2022 17.8+
VS Code最新版
Rider2023.3+

Microsoft Testing Platform

xUnit 3.x 預設啟用 Microsoft Testing Platform:

<PropertyGroup>
  <EnableMicrosoftTestingPlatform>true</​EnableMicrosoftTestingPlatform>
  <OutputType>Exe</​OutputType>
</​PropertyGroup>

效能改進

xUnit 3.x 帶來的效能改進:

  1. 獨立進程執行:測試在獨立進程中執行,更好的隔離性
  2. 改進的並行演算法:更智慧的負載平衡
  3. 更快的啟動時間:可執行檔直接執行
  4. 更好的記憶體隔離:減少測試之間的干擾

參考資源

原始文章

本技能內容提煉自「老派軟體工程師的測試修練 - 30 天挑戰」系列文章:

官方文件

範例參考

請參考同目錄下的範例檔案:

  • templates/​xunit-v3-project.csproj - xUnit 3.x 專案設定範本
  • templates/​upgrade-checklist.md - 升級檢查清單
  • templates/​code-migration-examples.cs - 程式碼遷移範例
  • templates/​new-features-examples.cs - 新功能使用範例

More skills from majiayu000

xiaohongshu logo
majiayu000/claude-arsenal

xiaohongshu

xiaohongshu

286 148
View
agent-task-conductor logo
majiayu000/claude-skill-registry

agent-task-conductor

Conduct multi-agent task orchestration and workflow coordination.

663 1
View
conductor-setup logo
majiayu000/claude-skill-registry

conductor-setup

Initialize project with Conductor artifacts (product definition,

663 1
View
animation-designer logo
majiayu000/claude-skill-registry

animation-designer

Expert in web animations, transitions, and motion design using Framer Motion and CSS

663 1
View
diagramming logo
majiayu000/claude-skill-registry

diagramming

Creates Mermaid and ASCII diagrams for flowcharts, architecture, ERDs, state machines, mindmaps, and more. Use when user mentions diagram, flowchart, mermaid, ASCII diagram, text diagram, terminal diagram, visualize, C4, mindmap, architecture diagram, sequence diagram, ERD, or needs visual docume...

663 1
View
h3-pg logo
majiayu000/claude-skill-registry-data

h3-pg

PostgreSQL bindings for H3 hexagonal grid system. Use when working with H3 cells in Postgres, including spatial indexing, geometry/geography integration, and raster analysis.

23 1
View
conductor-development logo
majiayu000/claude-skill-registry

conductor-development

Context-Driven Development skill for projects using Conductor. Use this skill when you detect a `conductor/` directory in the project, when working on tasks defined in a `plan.md` file, or when the user asks about tracks, specs, or plans. Automatically applies TDD workflow, tracks task completion...

663 1
View
conductor-status logo
majiayu000/claude-skill-registry

conductor-status

Display project status, active tracks, and next actions

663 1
View
dockerization logo
majiayu000/claude-skill-registry

dockerization

Official Stakpak application containerization standard operating procedure, a step-by-step guidline to properly dockerize applications. This is a rule book curated by the Stakpak Team.

663 1
View

Popular AI tools

Kaiber logo
Video

Kaiber

Generate, edit, and beat-sync AI video with leading models in one workspace.

Paid
View
Vimcal logo
Productivity

Vimcal

The world's fastest calendar for remote work

Free
View

Transform Your Design with AI Designer by ImgCreator.ai

Freemium
View
Akool AI logo
Content & writing

Akool AI

Revolutionizing Video Production with AI-Powered Creativity

Paid
View

Extend an image past the frame and let AI fill the new aspect ratio.

Freemium
View
StarByFace logo
Security

StarByFace

Discover your celebrity doppelgänger with StarByFace!

Free
View
C

ChainClarity explains 700+ crypto whitepapers in plain English, with layered summaries, comparisons, research tools, alerts, and a $4.99 Pro plan.

Freemium
View
Opus Clip logo
Coding & apps

Opus Clip

Opus.ai: Revolutionize Your Web Experience

Free
View