次の ASP.NET Core統合テスト カスタムWebApplicationFactory
を使用しています
public class CustomWebApplicationFactory<TEntryPoint> : WebApplicationFactory<TEntryPoint>
where TEntryPoint : class
{
public CustomWebApplicationFactory()
{
this.ClientOptions.AllowAutoRedirect = false;
this.ClientOptions.BaseAddress = new Uri("https://localhost");
}
public ApplicationOptions ApplicationOptions { get; private set; }
public Mock<IClockService> ClockServiceMock { get; private set; }
public void VerifyAllMocks() => Mock.VerifyAll(this.ClockServiceMock);
protected override TestServer CreateServer(IWebHostBuilder builder)
{
this.ClockServiceMock = new Mock<IClockService>(MockBehavior.Strict);
builder
.UseEnvironment("Testing")
.ConfigureTestServices(
services =>
{
services.AddSingleton(this.ClockServiceMock.Object);
});
var testServer = base.CreateServer(builder);
using (var serviceScope = testServer.Host.Services.CreateScope())
{
var serviceProvider = serviceScope.ServiceProvider;
this.ApplicationOptions = serviceProvider.GetRequiredService<IOptions<ApplicationOptions>>().Value;
}
return testServer;
}
}
うまくいくように見えますが、問題はConfigureTestServices
メソッドが呼び出されないため、モックがIoCコンテナに登録されないことです。完全なソースコード here を見つけることができます。
public class FooControllerTest : IClassFixture<CustomWebApplicationFactory<Startup>>, IDisposable
{
private readonly HttpClient client;
private readonly CustomWebApplicationFactory<Startup> factory;
private readonly Mock<IClockService> clockServiceMock;
public FooControllerTest(CustomWebApplicationFactory<Startup> factory)
{
this.factory = factory;
this.client = factory.CreateClient();
this.clockServiceMock = this.factory.ClockServiceMock;
}
[Fact]
public async Task Delete_FooFound_Returns204NoContent()
{
this.clockServiceMock.SetupGet(x => x.UtcNow).ReturnsAsync(new DateTimeOffset.UtcNow);
var response = await this.client.DeleteAsync("/foo/1");
Assert.Equal(HttpStatusCode.NoContent, response.StatusCode);
}
public void Dispose() => this.factory.VerifyAllMocks();
}
偽のスタートアップを作成する必要があります。
public class FakeStartup : Startup
{
public FakeStartup(IConfiguration configuration)
: base(configuration)
{
}
public override void ConfigureServices(IServiceCollection services)
{
base.ConfigureServices(services);
// Your fake go here
//services.AddScoped<IService, FakeService>();
}
}
次に、IClassFixture<CustomWebApplicationFactory<FakeStartup>>
で使用します。
オリジナルのConfigureServices
仮想メソッドを作成してください。
これを処理する最良の方法は、テスト中に置き換える必要があるStartup
の一部を除外することです。たとえば、ConfigureServices
でservices.AddDbContext<MyContext>(...);
を直接呼び出す代わりに、次のような仮想プライベートメソッドを作成します。
private virtual void ConfigureDatabase(IServiceCollection services)
{
services.AddDbContext<MyContext>(...);
}
次に、テストプロジェクトで、SUTのTestStartup
クラスから派生するStartup
のようなクラスを作成します。次に、これらの仮想メソッドをオーバーライドして、テストサービスやモックなどをサブインします。
最後に、次のようにします。
builder
.UseEnvironment("Testing")
.UseStartup<TestStartup>();