• システム開発に関わる内容をざっくりと書いていく

【C#】WebApplicationFactoryでケースごとに個別にモックを適用した結合テストの実装

C# の xUnit を使用し、
WebApplicationFactory を活用して 個別ケースごとに異なるモックを適用 する結合テストの実装方法を解説。

本記事のポイント

  • WebApplicationFactory<T> を活用し、本番に近い形でWeb APIの動作をテスト
  • モック(Moq)を個別テストケースごとに適用
  • テストごとに異なるモックの振る舞いを定義

WebApplicationFactory の基本構造

まずは、基本的な WebApplicationFactory のカスタマイズ方法。

1 SampleWebApplicationFactory.cs(カスタムWebApplicationFactory)

// SampleWebApplicationFactory.cs
using System;
using Microsoft.AspNetCore.Mvc.Testing;
using Microsoft.Extensions.DependencyInjection;

public class SampleWebApplicationFactory : WebApplicationFactory<Program>
{
protected override void ConfigureWebHost(IWebHostBuilder builder)
{
builder.ConfigureServices(services =>
{
// 個別テストごとにサービスを上書きするため、ここではデフォルトの設定のみ
});
}
}

ConfigureWebHost で DIコンテナをカスタマイズ
モックは個別のテストケースで適用するため、ここでは設定を追加しない

WebApplicationFactory を活用したモック適用

次に、テストケースごとに異なるモックをセット する方法。

1 SampleTest.cs(結合テスト)

// SampleTest.cs
using System.Net.Http;
using System.Text;
using System.Threading.Tasks;
using Microsoft.Extensions.DependencyInjection;
using Moq;
using Xunit;

public class SampleTest : IClassFixture<SampleWebApplicationFactory>
{
private readonly HttpClient _httpClient;
private readonly SampleWebApplicationFactory _factory;

public SampleTest(SampleWebApplicationFactory factory)
{
_factory = factory;
_httpClient = factory.CreateClient();
}

[Fact]
public async Task UploadFile_Should_Call_FileUploader()
{
// Arrange
var fileUploaderMock = new Mock<IFileUploader>();
fileUploaderMock.Setup(u => u.UploadAsync(It.IsAny<byte[]>())).ReturnsAsync(true);

using var factory = _factory.WithWebHostBuilder(builder =>
{
builder.ConfigureServices(services =>
{
services.AddSingleton(fileUploaderMock.Object);
});
});

var client = factory.CreateClient();
var testFileContent = new StringContent("test data", Encoding.UTF8, "application/octet-stream");

// Act
var response = await client.PostAsync("/api/upload", testFileContent);

// Assert
fileUploaderMock.Verify(u => u.UploadAsync(It.IsAny<byte[]>()), Times.Once);
}

[Fact]
public async Task SendMail_Should_Call_MailSender()
{
// Arrange
var mailSenderMock = new Mock<IMailSender>();
mailSenderMock.Setup(m => m.SendAsync(It.IsAny<string>(), It.IsAny<string>())).ReturnsAsync(true);

using var factory = _factory.WithWebHostBuilder(builder =>
{
builder.ConfigureServices(services =>
{
services.AddSingleton(mailSenderMock.Object);
});
});

var client = factory.CreateClient();
var emailContent = new StringContent("{\"to\":\"test@example.com\", \"message\":\"Hello\"}", Encoding.UTF8, "application/json");

// Act
var response = await client.PostAsync("/api/sendmail", emailContent);

// Assert
mailSenderMock.Verify(m => m.SendAsync(It.IsAny<string>(), It.IsAny<string>()), Times.Once);
}
}

テストごとに異なるモックを適用するメリット

方法特徴
Factory内で一括モック– すべてのテストで共通のモックを適用する場合に有効
テストごとに個別モック– 各テストで異なる振る舞いを定義できる
FactoryのDIコンテナを上書きWithWebHostBuilder を利用して、テスト単位でDIを変更

テストごとに異なるモックを適用することで、より柔軟なテストが可能!
モックの振る舞いをカスタマイズし、様々なケースを検証!

まとめ

WebApplicationFactory を使って結合テスト

  • HttpClient を使い、エンドツーエンドのAPIテスト
  • 本番に近い形で 実際のリクエスト/レスポンスをテスト

テストごとに異なるモックを適用

  • WithWebHostBuilder を利用し、個別ケースごとにモックを設定
  • Moq を利用して外部依存を排除し、振る舞いを検証

具体的なテストケース

  1. ファイルアップロードAPIで IFileUploader.UploadAsync の呼び出しを検証
  2. メール送信APIで IMailSender.SendAsync の呼び出しを検証

これにより、テストごとに異なるモックを適用しながら、
Web API の振る舞いを正確に検証できるテストフレームワークを構築!