引言
在現(xiàn)代化的軟件開發(fā)中,單元測(cè)試和集成測(cè)試是確保代碼質(zhì)量和可靠性的關(guān)鍵部分。ASP.NET Core
社區(qū)內(nèi)提供了強(qiáng)大的單元測(cè)試框架,xUnit
是其中之一,它提供了簡(jiǎn)單、清晰和強(qiáng)大的測(cè)試功能,編寫單元測(cè)試有許多優(yōu)點(diǎn);有助于回歸、提供文檔及輔助良好的設(shè)計(jì)。下面幾節(jié)我們來(lái)深入淺出探討如何使用 xUnit
進(jìn)行 ASP.NET Core
應(yīng)用程序的單元測(cè)試和集成測(cè)試。
內(nèi)容大綱:
xUnit 簡(jiǎn)介
xUnit.net
是一個(gè)免費(fèi)、開源、面向社區(qū)的.NET
單元測(cè)試工具。由NUnit v2
的原始發(fā)明者編寫,xUnit.net
是用于C#
和F#
(其他.NET
語(yǔ)言可能也可以使用,但不受支持)的最新技術(shù)單元測(cè)試。xUnit.net
可與Visual Studio
、Visual Studio Code
、ReSharper
、CodeRush
和TestDriven.NET
一起使用。它是.NET
基金會(huì)的一部分,并遵守其行為準(zhǔn)則。其許可協(xié)議為 Apache 2(為 OSI 批準(zhǔn)的許可協(xié)議)。
xUnit.net 官方網(wǎng)站
創(chuàng)建單元測(cè)試項(xiàng)目
在單元測(cè)試中通常要遵循AAA
模式,也就是 Arrange
、Act
、Assert
,這是一種常見的測(cè)試組織結(jié)構(gòu)。
-
Arrange(準(zhǔn)備)
: 在這個(gè)階段,將設(shè)置測(cè)試的前提條件,初始化對(duì)象、設(shè)置輸入?yún)?shù)等。簡(jiǎn)單講就是準(zhǔn)備測(cè)試環(huán)境,確保被測(cè)代碼在正確的上下文中執(zhí)行。 -
Act(執(zhí)行)
: 在這個(gè)階段,會(huì)執(zhí)行要測(cè)試的代碼或方法。這是針對(duì)被測(cè)代碼的實(shí)際調(diào)用或操作。 -
Assert
: 在這個(gè)階段,會(huì)驗(yàn)證被測(cè)代碼的行為是否符合預(yù)期。檢查實(shí)際結(jié)果與期望結(jié)果是否一致,如果不一致則測(cè)試失敗。
示例:
[Fact]
public void Add_EmptyString_ReturnsZero()
{
// Arrange
var stringCalculator = new StringCalculator();
// Act
var actual = stringCalculator.Add("");
// Assert
Assert.Equal(0, actual);
}
可讀性是編寫單元測(cè)試最重要的方面之一,在測(cè)試中分離這些操作 都明確地突出調(diào)用代碼所需的依賴項(xiàng)、調(diào)用代碼的方式以及嘗試斷言的內(nèi)容,讓測(cè)試盡可能具有可讀性。
好了理解了這個(gè)核心概念我們可以先創(chuàng)建項(xiàng)目一步步的練習(xí)了。
用 VS 創(chuàng)建單元測(cè)試項(xiàng)目
在項(xiàng)目創(chuàng)建完之后我們可以簡(jiǎn)單瀏覽一下 xUnit
單元測(cè)試項(xiàng)目裝了那些 nuget
依賴,做到對(duì)項(xiàng)目有個(gè)簡(jiǎn)單的了解
<ItemGroup>
<PackageReference Include="coverlet.collector" Version="6.0.0" />
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.8.0" />
<PackageReference Include="xunit" Version="2.5.3" />
<PackageReference Include="xunit.runner.visualstudio" Version="2.5.3" />
</ItemGroup>
下面我們創(chuàng)建一個(gè)簡(jiǎn)單的數(shù)據(jù)計(jì)算類。
- 創(chuàng)建數(shù)學(xué)計(jì)算類
public class MathCalculator
{
public int Add(int a, int b)
{
return a + b;
}
}
- 創(chuàng)建數(shù)據(jù)計(jì)算測(cè)試類
public class MathCalculatorTests
{
[Fact]
public void Add_TwoNumbers_ReturnSum()
{
// Arrange
var calculator = new MathCalculator();
// Act
var result = calculator.Add(3, 5);
// Assert
Assert.Equal(8, result);
}
}
測(cè)試一下,測(cè)試類庫(kù)右鍵->運(yùn)行測(cè)試
可以看到 我們的單元測(cè)試通過。
單元測(cè)試命名規(guī)范
本著代碼自文檔的原則,測(cè)試的名稱建議應(yīng)包括三個(gè)部分:
- 要測(cè)試的方法的名稱。
- 測(cè)試的方案。
- 調(diào)用方案時(shí)的預(yù)期行為。
示例
[Fact]
public void Add_TwoNumbers_ReturnSum()
{
// Arrange
var calculator = new MathCalculator();
// Act
var result = calculator.Add(3, 5);
// Assert
Assert.Equal(8, result);
}
要測(cè)試的方法名稱是 MathCalculator
中的 Add 方法,測(cè)試的方案是傳兩個(gè)數(shù),預(yù)期是返回兩數(shù)之和 按照上面的測(cè)試名稱的命名規(guī)則可以命名為Add_TwoNumbers_ReturnSum
。
單元測(cè)試最佳命名規(guī)范應(yīng)該包括三個(gè)關(guān)鍵部分:要測(cè)試的方法的名稱、測(cè)試的場(chǎng)景,以及調(diào)用該場(chǎng)景時(shí)的預(yù)期行為。良好的命名標(biāo)準(zhǔn)能清晰表達(dá)測(cè)試意圖,提供有效文檔,便于他人理解代碼行為和快速定位問題。
單元測(cè)試最佳實(shí)踐
將方法標(biāo)記為測(cè)試方法在
xUnit
中有兩個(gè)屬性,Fact
和Theory
Fact 屬性
在方法上我們看到有一個(gè) Attribute
[Fact] ,[Fact] 屬性是 xUnit 中最基本的測(cè)試屬性之一,用于標(biāo)記一個(gè)方法作為一個(gè)無(wú)需參數(shù)且不返回任何內(nèi)容的測(cè)試方法。被標(biāo)記為 [Fact] 的方法將會(huì)被 xUnit
框架識(shí)別并執(zhí)行.
Theory 屬性
Theory
屬性用于標(biāo)記一個(gè)測(cè)試方法,該方法可以接受參數(shù)并運(yùn)行多次,每次運(yùn)行時(shí)使用不同的參數(shù)值。Theory 屬性通常用于數(shù)據(jù)驅(qū)動(dòng)測(cè)試,允許在同一個(gè)測(cè)試方法中使用不同的輸入數(shù)據(jù)進(jìn)行測(cè)試.
InlineData 屬性
[InlineData] 屬性指定這些輸入 Theory 標(biāo)記的測(cè)試方法的參數(shù)值。
示例:
[Theory]
[InlineData(-1)]
[InlineData(0)]
[InlineData(1)]
public void IsPrime_ValuesLessThan2_ReturnFalse(int value)
{
var result = _primeService.IsPrime(value);
Assert.False(result, $"{value} should not be prime");
}
InlineData
適用于靜態(tài)、硬編碼的測(cè)試數(shù)據(jù)集合,適合于簡(jiǎn)單且固定的測(cè)試場(chǎng)景。
MemberData 屬性
MemberData
屬性是 xUnit
中用于數(shù)據(jù)驅(qū)動(dòng)測(cè)試的一種方式,它允許從一個(gè)字段、屬性或方法中獲取測(cè)試數(shù)據(jù),并將這些數(shù)據(jù)傳遞給測(cè)試方法進(jìn)行多次測(cè)試。通過 MemberData
屬性,可以更靈活地管理和提供測(cè)試數(shù)據(jù),適用于需要?jiǎng)討B(tài)生成測(cè)試數(shù)據(jù)的情況。
使用方式
- 標(biāo)記測(cè)試方法:使用 [Theory] 屬性標(biāo)記測(cè)試方法,以便接受從 MemberData 屬性提供的測(cè)試數(shù)據(jù)。
- 準(zhǔn)備測(cè)試數(shù)據(jù):創(chuàng)建一個(gè)公共靜態(tài)字段、屬性或方法,該字段、屬性或方法返回一個(gè) IEnumerable<object[]> 對(duì)象,其中每個(gè) object[] 對(duì)象代表一組測(cè)試數(shù)據(jù)。
- 傳遞測(cè)試數(shù)據(jù):在 MemberData 屬性中指定要使用的數(shù)據(jù)源,從而將數(shù)據(jù)傳遞給測(cè)試方法。
示例
public static IEnumerable<object[]> GetComplexTestData()
{
yield return new object[] { 10, 5, 15 }; // 測(cè)試數(shù)據(jù) 1
yield return new object[] { -3, 7, 4 }; // 測(cè)試數(shù)據(jù) 2
yield return new object[] { 0, 0, 0 }; // 測(cè)試數(shù)據(jù) 3
// 可以根據(jù)需要繼續(xù)添加更多的測(cè)試數(shù)據(jù)
}
[Theory]
[MemberData(nameof(GetComplexTestData))]
public void Add_TwoNumbers_ReturnsSumofNumbers01(int first, int second, int sum)
{
// Arrange
var calculator = new MathCalculator();
// Act
var result = calculator.Add(first, second);
// Assert
Assert.Equal(sum, result);
}
MemberData
適用于動(dòng)態(tài)、靈活的測(cè)試數(shù)據(jù)集合,適合于需要從外部源動(dòng)態(tài)獲取測(cè)試數(shù)據(jù)的情況。
自定義屬性
除了上面提到的 InlineData
和MemberData
之外還可以有更加靈活的方式繼承DataAttribute
實(shí)現(xiàn)自定義的Attribute
。
我們來(lái)做一個(gè)實(shí)現(xiàn)和上面一樣的需求
- 實(shí)現(xiàn) Custom Attribute
[AttributeUsage(AttributeTargets.Method, AllowMultiple = true)]
public class CustomDataAttribute : DataAttribute
{
private readonly int _first;
private readonly int _second;
private readonly int _sum;
public CustomDataAttribute(int first, int second, int sum)
{
_first = first;
_second = second;
_sum = sum;
}
public override IEnumerable<object[]> GetData(MethodInfo testMethod)
{
yield return new object[] { _first, _second, _sum };
}
}
- 用例
[Theory]
[CustomData(1, 2, 3)]
[CustomData(2, 3, 5)]
public void Add_TwoNumbers_ReturnSum03(int num1, int num2, int expectedSum)
{
// Arrange
var calculator = new MathCalculator();
// Act
var result = calculator.Add(num1, num2);
// Assert
Assert.Equal(expectedSum, result);
}
自定義屬性相較于使用 InlineData
和 MemberData
有以下優(yōu)勢(shì):
-
靈活性:自定義屬性允許您實(shí)現(xiàn)更復(fù)雜的邏輯來(lái)動(dòng)態(tài)生成測(cè)試數(shù)據(jù),可以從不同數(shù)據(jù)源中獲取數(shù)據(jù),實(shí)現(xiàn)更靈活的數(shù)據(jù)驅(qū)動(dòng)測(cè)試。
-
重用性:通過自定義屬性,您可以將相同的測(cè)試數(shù)據(jù)邏輯應(yīng)用于多個(gè)測(cè)試方法,提高測(cè)試代碼的重用性和可維護(hù)性。
-
可擴(kuò)展性:自定義屬性可以根據(jù)需求進(jìn)行定制和擴(kuò)展,適應(yīng)不同的測(cè)試場(chǎng)景和數(shù)據(jù)需求,使得測(cè)試數(shù)據(jù)的生成更具靈活性。
-
可讀性:通過自定義屬性,可以使測(cè)試代碼更具可讀性和表達(dá)力,更清晰地表達(dá)測(cè)試數(shù)據(jù)的來(lái)源和意圖。
盡管使用 InlineData
和 MemberData
可以滿足大多數(shù)簡(jiǎn)單的測(cè)試數(shù)據(jù)需求,但當(dāng)需要更復(fù)雜的數(shù)據(jù)生成邏輯、數(shù)據(jù)源、或者對(duì)測(cè)試數(shù)據(jù)進(jìn)行處理時(shí),使用自定義屬性會(huì)更具優(yōu)勢(shì),能夠更好地滿足個(gè)性化的測(cè)試需求。
在測(cè)試中應(yīng)避免邏輯
[Theory]的出現(xiàn)就是為了避免我們?cè)趩卧獪y(cè)試時(shí)編寫一些額外的邏輯,造成測(cè)試之外的一些錯(cuò)誤。
編寫單元測(cè)試時(shí),請(qǐng)避免手動(dòng)字符串串聯(lián)、邏輯條件(例如 if、while、for 和 switch)以及其他條件。
錯(cuò)誤示范:
[Fact]
public void Add_TwoNumbers_ReturnsSumofNumbers02()
{
// Arrange
var calculator = new MathCalculator();
var testData = new List<(int, int, int)>
{
(1, 2, 3),
(2, 3, 5),
(3, 4, 7)
};
// Act & Assert
foreach (var (first, second, sum) in testData)
{
var result = calculator.Add(first, second);
Assert.Equal(sum, result);
}
}
此處用了 forEach
循環(huán)來(lái)批量斷言,違反了單元測(cè)試的最佳實(shí)踐。
測(cè)試中應(yīng)避免邏輯的好處是:
- 降低在測(cè)試中引入 bug 的可能性。
- 專注于最終結(jié)果,而不是實(shí)現(xiàn)細(xì)節(jié)。
ITestOutputHelper 控制臺(tái)輸出
在 xUnit 中我們利用 Console.WriteLine
輸出時(shí)發(fā)現(xiàn)什么也不會(huì)顯示,在 xUnit 單元測(cè)試項(xiàng)目中我們需要利用ITestOutputHelper
。ITestOutputHelper
是 xUnit 中的一個(gè)接口,用于在單元測(cè)試中輸出信息。通過 ITestOutputHelper
,您可以在測(cè)試運(yùn)行時(shí)將調(diào)試信息、日志信息等輸出到測(cè)試結(jié)果中,方便調(diào)試和查看測(cè)試過程中的輸出信息。
調(diào)試
再要測(cè)試的方法上右鍵選擇調(diào)試測(cè)試,或者點(diǎn)擊方法上面的小點(diǎn)
最后
本篇文章簡(jiǎn)單的講了單元測(cè)試的基礎(chǔ)知識(shí),讓大家先對(duì)單元測(cè)試有個(gè)基本的概念,這些用在具體的項(xiàng)目中顯然是不夠的,后面的章節(jié)我們聊一下 TDD
,Fake
管理,Log
日志輸出,單元測(cè)試覆蓋率,WebApi
的集成測(cè)試,DependencyInjection
,Bogus
,還有 Devops
的單元測(cè)試等知識(shí)。文章來(lái)源:http://www.zghlxwxcb.cn/news/detail-844119.html
本文完整源代碼文章來(lái)源地址http://www.zghlxwxcb.cn/news/detail-844119.html
到了這里,關(guān)于.Net單元測(cè)試xUnit和集成測(cè)試指南(1)的文章就介紹完了。如果您還想了解更多內(nèi)容,請(qǐng)?jiān)谟疑辖撬阉鱐OY模板網(wǎng)以前的文章或繼續(xù)瀏覽下面的相關(guān)文章,希望大家以后多多支持TOY模板網(wǎng)!