這是昨天解決的一個問題,針對一個 web api 的客戶端代理類寫整合測試,既要測試 web api,又要測試 web api 客戶端。
測試 web api,就要在執行測試時自動啟動 web api 站點,asp.net core 中的 TestServer 就是為此而生,而且與 asp.net core 結合的天衣無縫,不僅自己可以通過I WebHostBuilder 配置站點,而且可以直接使用 web api 站點的 Startup 類。
對於測試時的資料模擬,可以通過 EF Core 提供的 InMemory database ,使用起來超級簡單,只需在依賴注入 DbContext 時使用 options.UseInMemoryDatabase() 。
而對於 TestServer 的統一管理(配置、啟動、銷燬),對所測試的類的依賴注入,可以藉助 xUnit.net 的 fixture class ,詳見 Shared Context between Tests 。
xUnit.net fixture class 的示例程式碼如下:
public class WebApiTestFixture : IDisposable { private readonly TestServer _testServer; public WebApiTestFixture() { IWebHostBuilder webHostBuilder = WebHost.CreateDefaultBuilder() .ConfigureLogging((logging) => { logging.AddConsole(); }) .ConfigureServices(services => { services.AddSingleton<IMemcachedClient, NullMemcachedClient>(); services.AddDbContext<UCenterDbContext>(options => { options.UseInMemoryDatabase("UCenter"); }); }) .UseStartup<Startup>(); _testServer = new TestServer(webHostBuilder); ServerServices = _testServer.Host.Services; ProvisionData(ServerServices.GetRequiredService<UCenterDbContext>()); ConfigureClientServices(_testServer.CreateClient()); } public IServiceProvider ClientServices { get; private set; } public IServiceProvider ServerServices { get; private set; } private void ConfigureClientServices(HttpClient httpClient) { IServiceCollection services = new ServiceCollection(); services.AddSingleton<IMemcachedClient, NullMemcachedClient>(); services.AddLogging(builder => builder.AddConsole()); services.AddSingleton(httpClient); services.AddSingleton<IUCenterService, UCenterService>(); ClientServices = services.BuildServiceProvider(); } private void ProvisionData(UCenterDbContext dbContext) { //dbContext.Add(); dbContext.SaveChanges(); } public void Dispose() { _testServer.Dispose(); } }
整合測試的示例程式碼如下:
public class UCenterServiceTests : IClassFixture<WebApiTestFixture> { private readonly IUCenterService _ucenterService; private readonly UCenterDbContext _dbContext; public UCenterServiceTests(WebApiTestFixture fixture) { _ucenterService = fixture.ClientServices.GetService<IUCenterService>(); _dbContext = fixture.ServerServices.GetService<UCenterDbContext>(); } [Fact] public async Task GetUserTest() { var fakeUser = _dbContext.Users.FirstOrDefault();
var user = await _ucenterService.GetUser(u => u.DisplayName, fakeUser.DisplayName); user.ShouldNotBeNull(); user.BlogApp.ShouldBe(fakeUser.BlogApp); } }