使用 xUnit 快速編寫 .NET 應(yīng)用單元測試
在當(dāng)今快速迭代的軟件開發(fā)環(huán)境中,單元測試已成為保障代碼質(zhì)量和項(xiàng)目可靠性的重要基石,通過驗(yàn)證程序最小單元(如函數(shù)、方法、類等)的獨(dú)立行為,能夠在開發(fā)階段快速發(fā)現(xiàn)邏輯錯(cuò)誤或邊界條件問題。今天大姚將帶領(lǐng)大家使用 xUnit 快速編寫 .NET 應(yīng)用單元測試。

項(xiàng)目介紹
xUnit 是一個(gè)開源、免費(fèi)、以社區(qū)為中心的 .NET 單元測試框架,是用于 C# 和 F#(其他 .NET 語言可能也能運(yùn)行,但未提供官方支持)進(jìn)行單元測試的最新技術(shù)。xUnit 能夠與 Visual Studio、Visual Studio Code、ReSharper、CodeRush 和 TestDriven.NET 兼容。它是.NET 基金會(huì)的一部分,并遵循其行為準(zhǔn)則。
單元測試優(yōu)秀做法
微軟官方出品的 .NET 單元測試最佳做法:https://learn.microsoft.com/zh-cn/dotnet/core/testing/unit-testing-best-practices
(1) 避免基礎(chǔ)結(jié)構(gòu)依賴項(xiàng)。
(2) 以最精簡方式編寫通過測試。
(3) 避免使用魔法字符串。
(4) 避免在單元測試中編寫代碼邏輯。
(5) 遵循測試命名標(biāo)準(zhǔn):
- 要測試的方法的名稱。
- 測試方法的情境。
- 調(diào)用方案時(shí)的預(yù)期行為。
命名標(biāo)準(zhǔn)非常重要,因?yàn)樗鼈冇兄诒磉_(dá)測試目的和應(yīng)用程序。測試不僅僅是確保代碼正常工作。它們還提供文檔。只需查看單元測試套件,即可推斷代碼的行為,不必查看代碼本身。此外,測試失敗時(shí),可以確切地看到哪些方案不符合預(yù)期。

單元測試基本步驟
我們?cè)诰帉憜卧獪y試的時(shí)候通常遵循 3A 模式(Arrange-Act-Assert),這是單元測試的核心方法論:
- Arrange(準(zhǔn)備階段): 該階段用于模擬數(shù)據(jù)、初始化對(duì)象等準(zhǔn)備工作。
- Act(執(zhí)行階段): 該階段用于準(zhǔn)備好的數(shù)據(jù)調(diào)用要測試的最小單元方法。
- Assert(斷言階段): 該階段是單元測試中的驗(yàn)證環(huán)節(jié),它通過將目標(biāo)方法返回的實(shí)際結(jié)果與預(yù)期結(jié)果進(jìn)行比對(duì),來判定測試是否通過。

創(chuàng)建單元測試項(xiàng)目
因?yàn)?xUnit 框架與 Visual Studio 是兼容的,我們可以直接在 Visual Studio 中搜索:xUnit 測試項(xiàng)目 模板,然后創(chuàng)建一個(gè)名為:xUnitExercise 的 .NET 9 單元測試項(xiàng)目。


編寫簡單的單元測試
public class UnitTest
{
/// <summary>
/// 測試 Calculator 的 Add 方法功能
/// 驗(yàn)證兩個(gè)正數(shù)相加返回正確的和
/// </summary>
[Fact]// 標(biāo)識(shí)這是一個(gè)獨(dú)立的測試用例
public void Add_TwoPositiveNumbers_ReturnsCorrectSum()
{
// ===== Arrange(準(zhǔn)備階段) =====
var calculator = new Calculator();
int num1 = 5;
int num2 = 7;
int expected = 12;
// ===== Act(執(zhí)行階段) =====
int actual = calculator.Add(num1, num2);
// ===== Assert(斷言階段) =====
Assert.Equal(expected, actual);
}
/// <summary>
/// 測試 Calculator 的 Divide 方法異常處理
/// 驗(yàn)證除數(shù)為零時(shí)正確拋出 DivideByZeroException 異常
/// </summary>
[Fact]
public void Divide_ByZero_ThrowsDivideByZeroException()
{
// Arrange
var calculator = new Calculator();
int dividend = 10;
int divisor = 0; //觸發(fā)異常的除數(shù)
// Act & Assert
// 驗(yàn)證執(zhí)行除法時(shí)是否拋出特定異常
var exception = Assert.Throws<DivideByZeroException>(
() => calculator.Divide(dividend, divisor));
// 驗(yàn)證異常消息是否符合預(yù)期
Assert.Equal("除數(shù)不能為零", exception.Message);
}
/// <summary>
/// 參數(shù)化測試 Calculator 的 IsEven 方法功能
/// 驗(yàn)證不同輸入數(shù)值的奇偶判斷是否正確
/// </summary>
/// <param name="number">測試輸入值</param>
/// <param name="expected">預(yù)期結(jié)果(true=偶數(shù),false=奇數(shù))</param>
[Theory] // 標(biāo)識(shí)這是一個(gè)參數(shù)化測試
[InlineData(4, true)] // 測試數(shù)據(jù)1:偶數(shù)4,預(yù)期true
[InlineData(7, false)] // 測試數(shù)據(jù)2:奇數(shù)7,預(yù)期false
[InlineData(8, false)] // 測試數(shù)據(jù)3:偶數(shù)8,預(yù)期false 【這里是特意為了查看預(yù)期結(jié)果不一致的情況】
public void IsEven_Number_ReturnsCorrectResult(int number, bool expected)
{
// Arrange
var calculator = new Calculator();
// Act
bool actual = calculator.IsEven(number);
// Assert
Assert.Equal(expected, actual);
}
public class Calculator
{
public int Add(int a, int b)
{
return a + b;
}
public bool IsEven(int number)
{
return number % 2 == 0;
}
public double Divide(int dividend, int divisor)
{
if (divisor == 0)
throw new DivideByZeroException("除數(shù)不能為零");
return (double)dividend / divisor;
}
}
}運(yùn)行單元測試
選擇項(xiàng)目右鍵 => 運(yùn)行測試:


或者直接在對(duì)應(yīng)的方法正上方選擇 Run:


調(diào)試單元測試
選擇項(xiàng)目右鍵 => 運(yùn)行調(diào)試:


或者直接在對(duì)應(yīng)的方法正上方選擇 Debug:


項(xiàng)目源碼地址
更多項(xiàng)目實(shí)用功能和特性歡迎前往項(xiàng)目開源地址查看??,別忘了給項(xiàng)目一個(gè)Star支持??。
- GitHub開源地址:https://github.com/xunit/xunit
- 本文示例源碼地址:https://github.com/YSGStudyHards/DotNetExercises/tree/master/xUnitExercise




























