Asp-Net-Core學習筆記:單元測試和集成測試
1 前言
我在使用 AspNetCore 的這段時間內(nèi),看了很多開源項目和博客,發(fā)現(xiàn)各種 .Net 體系的新技術(shù)很多人都有關(guān)注和使用,但卻很少有人關(guān)注測試。
測試是軟件生命周期中的一個非常重要的階段,對于保證軟件的可靠性具有極其重要的意義。在應用程序的開發(fā)過程中,為了確保它的功能與預期一致,必須對其進行測試。這樣做不僅能夠確保功能正確執(zhí)行,同時能夠幫助開發(fā)人員盡早地發(fā)現(xiàn)并改正系統(tǒng)中所存在的缺陷(Bug),從而提高軟件的可靠性。測試應該覆蓋到軟件的所有功能,全面、細致的測試會在很大程度上節(jié)省軟件開發(fā)的成本;反之,不足的測試勢必會使軟件包含一些未發(fā)現(xiàn)的缺陷而投入運行,使用戶承擔軟件缺陷所造成的危險。
從測試所涉及的層次上,分為單元測試、集成測試和系統(tǒng)測試。單元測試是指驗證代碼段(如方法或函數(shù))功能的測試,通常由開發(fā)人員編寫相應的測試方法,以驗證代碼執(zhí)行后與預期結(jié)果是否一致;集成測試用于驗證具有依賴關(guān)系的多個模塊或組件是否能夠正常工作;系統(tǒng)測試是對整個系統(tǒng)進行全面測試,以確認系統(tǒng)正常運行并符合需求。
本文主要記錄一下單元測試和集成測試。
PS:本文只介紹簡單的測試方法,拋磚引玉,更深入的使用可以參考官方文檔,其中寫得很清楚。
2 .Net 中的測試
.Net 的測試項目,主要有幾種模板
- MSTest
- NUnit
- xUnit
本文選擇 xUnit 作為測試項目模板。
3 單元測試
從測試所涉及的層次上,分為單元測試、集成測試和系統(tǒng)測試。單元測試是指驗證代碼段(如方法或函數(shù))功能的測試,通常由開發(fā)人員編寫相應的測試方法,以驗證代碼執(zhí)行后與預期結(jié)果是否一致;集成測試用于驗證具有依賴關(guān)系的多個模塊或組件是否能夠正常工作;系統(tǒng)測試是對整個系統(tǒng)進行全面測試,以確認系統(tǒng)正常運行并符合需求。
- Arrange:為測試進行準備操作,如設(shè)置測試數(shù)據(jù)、變量和環(huán)境等。
- Act:執(zhí)行要測試的方法,如調(diào)用要測試的函數(shù)和方法。
- Assert:斷言測試結(jié)果,驗證被測試方法的輸出是否與預期的結(jié)果一致。
最簡單的例子
Controller 代碼
[ApiController]
public?class?DemoController?:?ControllerBase?{
??[HttpGet("[action]")]
??public?ApiResponse?Test()?{
????return?ApiResponse.Ok("version=2.0");
??}
}
單元測試代碼
public?class?DemoTests?{
??private?readonly?DemoController?_demoController;
??public?DemoTests()?{
????_demoController?=?new?DemoController();
????_demoController.ControllerContext?=?new?ControllerContext?{
??????HttpContext?=?new?DefaultHttpContext()
????};
??}
??[Fact]
??public?void?Test_Test()?{
????var?resp?=?_demoController.Test();
????Assert.Equal("version=2.0",?resp.Message);
??}
}
帶參數(shù)的 controller
實際開發(fā)中,Controller 會使用依賴注入,有很多參數(shù)
比如這個 controller ,用到了 EFCore 的 DbContext
public?class?StudentController?:?ControllerBase?{
??private?readonly?AppDbContext?_dbCtx;
??public?StudentController(AppDbContext?dbCtx)?{
????_dbCtx?=?dbCtx;
??}
??///?<summary>
??///?學生分數(shù)等級
??///?</summary>
??[HttpGet("[action]")]
??public?async?Task<List<StudentGrades>>?StudentScoreGrades()?{
????return?await?_dbCtx.StudentGrades.ToListAsync();
??}
}
這時候在單元測試里,就得用 mock 對象框架 來實現(xiàn)依賴模擬
這里用的是 Moq 這個庫,可以直接 nuget 安裝
public?class?StudentTests?{
??private?readonly?StudentController?_studentController;
??public?StudentTests()?{
????var?mockOptions?=?new?Mock<DbContextOptions<AppDbContext>>();
????var?mockDbContext?=?new?Mock<AppDbContext>(mockOptions.Object);
????_studentController?=?new?StudentController(mockDbContext.Object)?{
??????ControllerContext?=?new?ControllerContext?{
????????HttpContext?=?new?DefaultHttpContext()
??????}
????};
??}
??[Fact]
??public?async?Task?Test_StudentScoreGrades()?{
????var?resp?=?await?_studentController.StudentScoreGrades();
????Assert.True(resp.Count?>?0);
??}
}
PS:實際使用的時候,這個 mock 還是挺麻煩的,所以這些接口還是用后面的集成測試更好。
4 集成測試
集成測試能夠確保應用程序的組件正常工作,包括應用程序支持的基礎(chǔ)結(jié)構(gòu),如數(shù)據(jù)庫和文件系統(tǒng)等。ASP.NET Core提供了用于集成測試的組件,其中包含了用于測試的WebHost和內(nèi)存測試服務器TestServer。與單元測試不同,這里所有的依賴都是模擬出來的,在集成測試中,應使用與生產(chǎn)環(huán)境中一樣的真實組件,如數(shù)據(jù)庫和第三方庫等。
簡單說,單元測試是把所有組件都模擬一遍,集成測試是在真實的系統(tǒng)上進行測試,比如 AspNetCore 開發(fā)的接口,集成測試直接使用 HTTP 請求,獲取接口數(shù)據(jù),然后檢查這些返回數(shù)據(jù)是否符合條件。
開始前需要先安裝依賴
dotnet?add?Microsoft.AspNetCore.Mvc.Testing
公開被測試項目的 Program/Startup 類
集成測試使用 WebApplicationFactory 來啟動 AspNetCore 應用
所以需要在被測試的應用里做一點小修改,以便讓測試項目可以訪問到它的 Program 類
本文使用的被測試項目是基于 .Net6 的,修改它的 Program.cs ,添加以下代碼
public?partial?class?Program?{?}
使其 Program 類變成共有,讓測試項目能夠訪問到。
官網(wǎng)文檔上說還可以修改 .csproj ,添加配置
<ItemGroup>
??<InternalsVisibleTo?Include="MyTestProject"?/>
</ItemGroup>
但我試了好像沒有效果,所以還是用第一種方法比較方便。
PS:如果是 .Net6 之前的項目,就把 Startup 類改成 public
例子
以剛才的 Student 接口為例
用 HTTP 請求這幾個接口,然后判斷返回的響應頭里是否符合條件。
public?class?StudentTests?:?IClassFixture<WebApplicationFactory<Program>>?{
??private?readonly?WebApplicationFactory<Program>?_factory;
??public?StudentTests(WebApplicationFactory<Program>?factory)?{
????_factory?=?factory;
??}
??[Theory]
??[InlineData("/api/v2/Student/StudentScoreGrades")]
??[InlineData("/api/v2/Student/Height")]
??[InlineData("/api/v2/Student/Weight")]
??public?async?Task?Get_EndpointsReturnSuccessAndCorrectContentType(string?url)?{
????var?client?=?_factory.CreateClient();
????var?response?=?await?client.GetAsync(url);
????response.EnsureSuccessStatusCode();
????Assert.Equal("application/json;?charset=utf-8;?ver=2",?response.Content.Headers.ContentType?.ToString());
??}
}
更多示例
本文只介紹了 WebApi 的集成測試,實際開發(fā)可能還會對 MVC、Blazor 等項目進行測試
官方提供了一個實例項目: https://github.com/dotnet/AspNetCore.Docs.Samples/tree/main/test/integration-tests/IntegrationTestsSample
里面有很多集成測試的例子,可以參考一下。
5 運行測試
第一次測試需要先 Build 測試項目
本文使用的 IDE 是 Rider(VS 的話也是大同小異),其中提供了可視化的測試界面

之后寫完新的測試直接點擊旁邊的綠色三角形,就可以單獨運行這個新的測試。
6 參考資料
- https://learn.microsoft.com/zh-cn/aspnet/core/mvc/controllers/testing
- https://learn.microsoft.com/zh-cn/aspnet/core/test/integration-tests
