日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 编程资源 > 编程问答 >内容正文

编程问答

[Abp vNext 源码分析] - 18. 单元测试

發布時間:2023/12/4 编程问答 38 豆豆
生活随笔 收集整理的這篇文章主要介紹了 [Abp vNext 源码分析] - 18. 单元测试 小編覺得挺不錯的,現在分享給大家,幫大家做個參考.

簡介

ABP vNext 框架使用?xUnit?作為單元測試組件,官方的所有模塊都編寫了大量的?單元/集成測試?確保功能正常。由于 ABP vNext 模塊化系統的原因,開發人員在建立單元測試項目的時候需要集成?Volo.Abp.UnitTest?項目,這樣在執行單元測試的時候才不會缺少必要組件。

分析

ABP vNext 單元測試相關的類型最核心的是集成測試基類?AbpIntegratedTest?和 MVC 專用測試基類?AbpAspNetCoreIntegratedTestBase,這兩個基類核心工作就是初始化 IoC 容器并且初始化整個模塊系統,只不過后者對?控制器?相關的組件進行了初始化配置,讓開發人員可以針對?控制器?進行單元/集成測試。

從上圖可以看到兩個基類都繼承自?AbpTestBaseWithServiceProvider?基類,在這個基類里面將?IServiceProvider?作為一個抽象成員。這是因為 MVC 和測試基類的?ServiceProvider?來源不一樣,一個是 ABP vNext 根據?Application?類已注冊 IoC 容器構建的,另一個使用的是?IHost?測試主機內的?ServiceProvider。

單元測試執行本質上就是將測試類進行實例化,然后調用對應的單元測試方法,所以測試基類的初始化動作都是放在對應的無參構造函數。

雖然?Volo.Abp.UnitTest?也是單獨的一個項目,它的?AbpTestBaseModule?是沒有任何動作,僅僅是為了同其他項目保持一致,內部是沒有任何代碼。

using Volo.Abp.Modularity;namespace Volo.Abp {public class AbpTestBaseModule : AbpModule{} }

集成測試基類

一般來說,我們會直接從?AbpIntegratedTest?繼承并實現我們需要的單元測試基類,包括 ABP vNext 官方的默認模版也是這樣。集成測試基類的核心代碼很簡單,就是在無參構造函數的內部進行初始化動作,且在過程中按順序執行兩個生命周期方法。

簡易流程圖:

start=>start: 開始單元測試 op1=>operation: BeforeAddApplication() op2=>operation: 注冊模塊服務 op3=>operation: AfterAddApplication() op4=>operation: 模塊初始化 op5=>operation: 執行單元測試 end=>end: 銷毀相關資源

start->op1->op2->op3->op4->op5->end

public abstract class AbpIntegratedTest<TStartupModule> : AbpTestBaseWithServiceProvider, IDisposablewhere TStartupModule : IAbpModule {protected IAbpApplication Application { get; }protected override IServiceProvider ServiceProvider => Application.ServiceProvider;protected IServiceProvider RootServiceProvider { get; }protected IServiceScope TestServiceScope { get; }protected AbpIntegratedTest(){var services = CreateServiceCollection();BeforeAddApplication(services);var application = services.AddApplication<TStartupModule>(SetAbpApplicationCreationOptions);Application = application;AfterAddApplication(services);// 根據已有 IServiceCollection 創建 IoC 容器。RootServiceProvider = CreateServiceProvider(services);TestServiceScope = RootServiceProvider.CreateScope();// 使用子容器對 ABP 模塊系統進行初始化。application.Initialize(TestServiceScope.ServiceProvider);}// ... 其他代碼。 }

上述代碼可以看到默認的測試基類并沒有直接使用?RootServiceProvider,而是創建了一個子容器給 ABP vNext 使用,這主要是為了后續可以對容器進行銷毀操作,具體可一看下面的?Dispose()?方法。

public virtual void Dispose() {Application.Shutdown();TestServiceScope.Dispose();Application.Dispose(); }

總的來說,測試基類就是構建了一個?IAbpApplication?對象,根據傳入的?TStartupModule?模塊進入拓撲排序過程,依次執行各個模塊的生命周期配置。

MVC 測試基類

針對我們的 Http Api 層,如果需要對?Controller?進行測試的話,就需要從 MVC 測試基類繼承編寫單元/集成測試,各個類型的關系如下。

AbpTestBaseWithServiceProvider#ServiceProvider#GetService() : <T>#GetRequiredService() : <T>AbpIntegratedTest<TStartupModule>#BeforeAddApplication(IServiceCollection service)#SetAbpApplicationCreationOptions(AbpApplicationCreationOptions options)#AfterAddApplication(IServiceCollection services)#CreateServiceProvider(IServiceCollection service)+Dispose()AbpAspNetCoreIntegratedTestBase<TStartup>#TestServer Server#HttpClient Client-IHost host#CreateHostBuilder() : IHostBuilder#ConfigureServices(HostBuilderContext context, IServiceCollection services)#GetUrl_OfType_TController() : string#GetUrl_OfType_TController(string actionName) : string#GetUrl_OfType_TController(string actionName, object queryStringParamsAsAnonymousObject) : string+Dispose()IDispose<<interface>> IDispose

針對 AspNetCore 來說,ABP 創建了一個新的 Host 主機,在每次執行測試的時候會啟動一個新的 Web 服務器。(并不會創建真實服務,不存在端口占用問題)

在基類當中,ABP 定義了兩個屬性?Server?和?Client,它們都是 Mock 了對應的接口,方便后續的單元測試,這里的?ITestServerAccessor?接口是用于 Mock?AspNetCoreTestDynamicProxyHttpClientFactory?接口所需要的。

AspNetCoreTestDynamicProxyHttpClientFactory?接口是 ABP 底層進行動態代理所使用的,在請求遠程服務的時候會調用這個接口創建 HttpClient 對象。

protected AbpAspNetCoreIntegratedTestBase() {var builder = CreateHostBuilder();_host = builder.Build();_host.Start();Server = _host.GetTestServer();Client = _host.GetTestClient();ServiceProvider = Server.Services;ServiceProvider.GetRequiredService<ITestServerAccessor>().Server = Server; }

從 UML 類圖當中,可以看到基類定義了幾個?GetUrl()?方法,這幾個方法是根據 Controller 獲取對應的請求路徑。

這里我以一個?SampleController?控制器為例,它提供了一個 Index 方法,返回了一個頁面內容。針對它來說,我們編寫集成測試是這樣操作的。

public class SimpleController : AbpController {public ActionResult Index(){return Content("Index-Result");} }

集成測試

public class SimpleController_Tests : AbpAspNetCoreIntegratedTestBase<Startup> {protected virtual async Task<HttpResponseMessage> GetResponseAsync(string url, HttpStatusCode expectedStatusCode = HttpStatusCode.OK){using (var requestMessage = new HttpRequestMessage(HttpMethod.Get, url)){requestMessage.Headers.Add("Accept-Language", CultureInfo.CurrentUICulture.Name);var response = await Client.SendAsync(requestMessage);response.StatusCode.ShouldBe(expectedStatusCode);return response;}}protected virtual async Task<string> GetResponseAsStringAsync(string url, HttpStatusCode expectedStatusCode = HttpStatusCode.OK){using (var response = await GetResponseAsync(url, expectedStatusCode)){return await response.Content.ReadAsStringAsync();}}[Fact]public async Task ActionResult_ContentResult(){var result = await GetResponseAsStringAsync(GetUrl<SimpleController>(nameof(SimpleController.Index)));result.ShouldBe("Index-Result");} }

EF Core 的集成

在執行單元測試過程中,我們難免會對數據庫進行操作。這個時候不可能連接真實數據庫,就需要我們在測試基類當中進行一些初始化動作,將底層的數據庫鏈接改為 SQLite 的內存模式。

public class SampleEntityFrameworkCoreTestModule : AbpModule {private SqliteConnection _sqliteConnection;public override void ConfigureServices(ServiceConfigurationContext context){ConfigureInMemorySqlite(context.Services);}private void ConfigureInMemorySqlite(IServiceCollection services){// 建立鏈接并執行遷移。_sqliteConnection = CreateDatabaseAndGetConnection();// 使用 SQLite 作為 EF Provider。services.Configure<AbpDbContextOptions>(options =>{options.Configure(context =>{context.DbContextOptions.UseSqlite(_sqliteConnection);});});}public override void OnApplicationShutdown(ApplicationShutdownContext context){_sqliteConnection.Dispose();}private static SqliteConnection CreateDatabaseAndGetConnection(){// 使用 SQLite 的內存模式鏈接字符串。var connection = new SqliteConnection("Data Source=:memory:");connection.Open();var options = new DbContextOptionsBuilder<SampleMigrationsDbContext>().UseSqlite(connection).Options;// 執行遷移,構建表結構。using (var context = new SampleMigrationsDbContext(options)){context.GetService<IRelationalDatabaseCreator>().CreateTables();}return connection;} }

總結

ABP 的測試更偏向于集成測試,因為各個功能都依賴于模塊,所以在執行單元測試的時候會運行更長的時間。日常開發過程當中,我們更多地還是針對應用層進行測試就可以了,粒度更細的話也可以針對倉儲層、領域層、API 層?編寫測試即可。

為了保證項目質量,在開發完成之后編寫單元/集成測試是每個開發人員應做的工作。編寫單元/集成測試,雖然不能 100% 避免 BUG,但可以保證每次進行業務修改之后接口的正確性。

總結

以上是生活随笔為你收集整理的[Abp vNext 源码分析] - 18. 单元测试的全部內容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。