ASP.NET CoreでxUnitを導入する

はじめに

今回は、ユニットテストを行うためのフレームワークをASP.NET Coreに導入したかったので、xUnitというフレームワークを導入してみた。
この記事はその時の備忘録となる。

環境

Windows 11 Professional
Visual Studio 2022 Community
ASP.NET Core 8.0

xUnitとは

xUnitは、.NETアプリケーション用のテストフレームワークで、単体テストを効率的に実行するためのツール。
xUnitは、テストの作成や実行を簡単にし、コードの品質を向上させることを目的としている。
主な特徴として、アサーション、テストの分類、依存関係の管理などがあり、これにより、開発者は容易にテストケースを定義し、テストを自動化できる。

xUnitを選んだ理由について

Microsoftの.NET でのテストのページを見ると、

  • xUnit
  • NUnit
  • MSTest

などがあげられている。
この中で、xUnitを選んだ理由としては、現在最も使われているテストフレームワークのようだからだ。
下記を見たところ、xUnitが一番使われているそうなので、じゃあまあこれでという感じで使ってみることにした。

導入

例によって、ASP.NET CoreのWebアプリケーションが既にプロジェクトとしてある状態で始める。

下記のリポジトリは、以前よりサンプルとして使っており、今回もここに導入をしてみることにする。
https://github.com/katsuobushiFPGA/ASPCoreNetSample

プロジェクトの作成

1. ASP.NET Coreのプロジェクトを起動する

introduce-01

2. ソリューションを右クリック→「追加」→「新しいプロジェクト」を選択する

introduce-02

3.「xUnitテストプロジェクト」を選択し、「次へ」

introduce-03

4. プロジェクト名を入力して、「次へ」

introduce-04

ここでは、[テスト対象のプロジェクト名].Tests という名前にしている。

下記のドキュメントでも、そのような命名だったため、それに倣って作成している。
https://learn.microsoft.com/ja-jp/dotnet/core/testing/unit-testing-with-dotnet-test

5. 「追加情報」はデフォルトのままで「作成」を選択

introduce-05

6. ソリューションエクスプローラーにて、作成した「WebApplication2.Tests」が表示されていることを確認する

introduce-06

依存関係の参照/インストール

プロジェクト参照の追加

1. 「依存関係」を右クリック、「プロジェクト参照の追加」を選択する

dependency-01

2. 「WebApplication2(テスト対象のプロジェクト)」を選択し、「次へ」

dependency-02

3. 「依存関係」→「プロジェクト」に「WebApplication2(テスト対象のプロジェクト)」が入っていることを確認

dependency-03

ライブラリのインストール

Moqのインストール

  1. プロジェクトを右クリック、「NuGetパッケージの管理」を選択する
  2. Moq」をインストールする
  3. インストール完了後、依存関係に 「Moq」が入っていることを確認
dependency-04

xUnitのインストール

xUnit系はプロジェクト作成時にインストール済みのため不要。
ただし、最新バージョンより古いので、最新バージョンを使う必要があればアップデートをする。

dependency-05

テストの作成

今回はお試しで、Controllers/HomeController.csのテストを作成してみる。

テストプロジェクトのディレクトリ構成について

ディレクトリ構成のベストプラクティス的なものが見つからなかったので、ChatGPTに聞いてみた。


ASP.NET CoreのプロジェクトでxUnitを使ってコントローラのテストを行う場合、一般的なディレクトリ構成は以下のようにするのが推奨です。

  1. プロジェクトルート

    • アプリケーション本体のプロジェクト(例: YourApp)とテストプロジェクト(例: YourApp.Tests)を同じソリューションファイル内に置きます。
  2. ディレクトリ構成

   - YourSolution.sln
   ├── YourApp/
   │   ├── Controllers/
   │   ├── Services/
   │   └── Models/
   └── YourApp.Tests/
       ├── Controllers/
       ├── Services/
       └── Models/

テストプロジェクト(YourApp.Tests)はアプリケーションの構造を反映させ、テスト対象と同じ名前のディレクトリを作成します。たとえば、Controllersディレクトリ内にコントローラテストを配置し、ファイル名もコントローラ名に合わせると管理が容易です。

  1. 例: テストクラス名と配置

    • YourApp.Controllers.SampleController.cs に対して、テストプロジェクトには YourApp.Tests.Controllers.SampleControllerTests.cs のように配置します。
  2. 推奨する構成

    • 各テストクラスでは FactTheory を使って異なるテストケースを記述します。
    • Arrange-Act-Assert パターンに従うと、コードの意図が明確になり、メンテナンスが容易です。

ということなので、基本はテスト対象と同じディレクトリ構成にして、[テスト対象のファイル名]Tests.csという名前でファイルを作成していくのがよさそうか。

テスト対象の確認

テスト対象のHomeController.csを見ておこう。

Controllers/HomeController.cs
using Microsoft.AspNetCore.Mvc;
using System.Diagnostics;
using WebApplication2.Models;

namespace WebApplication2.Controllers
{
    using Json.Net;
    public class HomeController : Controller
    {
        private readonly ILogger<HomeController> _logger;

        public HomeController(ILogger<HomeController> logger)
        {
            _logger = logger;
        }

        public IActionResult Index()
        {

            return View();
        }

        public IActionResult Privacy()
        {
            return View();
        }

        [ResponseCache(Duration = 0, Location = ResponseCacheLocation.None, NoStore = true)]
        public IActionResult Error()
        {
            return View(new ErrorViewModel { RequestId = Activity.Current?.Id ?? HttpContext.TraceIdentifier });
        }
    }
}

このコントローラーをテストするために、ユニットテストを作成していく。

HomeControllerTests.csの作成

- WebAPplication2.sln
   └── WebApplication2.Tests
       └── Controllers/HomeControllerTests.cs

というパスで作成をした。

create-test-01

中身は以下となる。

Controllers/HomeControllerTests.cs
using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Mvc;
using Microsoft.Extensions.Logging;
using Moq;
using System.Diagnostics;
using WebApplication2.Controllers;
using WebApplication2.Models;
using Xunit;

namespace WebApplication2.Tests.Controllers
{
    public class HomeControllerTests
    {
        private readonly Mock<ILogger<HomeController>> _mockLogger;
        private readonly HomeController _controller;

        public HomeControllerTests()
        {
            // Arrange: ILogger<HomeController>をモック
            _mockLogger = new Mock<ILogger<HomeController>>();
            _controller = new HomeController(_mockLogger.Object);

            // HttpContextのモックを作成し、RequestIdを設定
            var httpContext = new DefaultHttpContext();
            httpContext.TraceIdentifier = "TestRequestId";

            // ControllerContextを設定してHttpContextを注入
            _controller.ControllerContext = new ControllerContext
            {
                HttpContext = httpContext
            };
        }

        [Fact]
        public void Index_ReturnsViewResult()
        {
            // Act
            var result = _controller.Index();

            // Assert
            var viewResult = Assert.IsType<ViewResult>(result);
            Assert.NotNull(viewResult);
        }

        [Fact]
        public void Privacy_ReturnsViewResult()
        {
            // Act
            var result = _controller.Privacy();

            // Assert
            var viewResult = Assert.IsType<ViewResult>(result);
            Assert.NotNull(viewResult);
        }

        [Fact]
        public void Error_ReturnsViewResultWithErrorViewModel()
        {
            // Act
            var result = _controller.Error();

            // Assert
            var viewResult = Assert.IsType<ViewResult>(result);
            var model = Assert.IsType<ErrorViewModel>(viewResult.Model);
            Assert.Equal("TestRequestId", model.RequestId);
        }
    }
}

テストの実行

テストも作成できたので、実行をしてみる。

1. 「WebApplication2.Tests(テストプロジェクト)」を右クリック、「テストの実行」を選択する

execute-test-01

2. 実行が完了すると結果が表示される

execute-test-02

これで、テストは完了できたので終了!

参考

おわりに

xUnitを使ってユニットテストの作成を行った。
AIがでてからテストケースの生成はかなりし易くなったと感じるので、ちゃんとテストを書いていきたい。
次回以降は、vue, vite, TypeScriptをフロントエンドに入れてみたいなあと思う。
このあたりができたら、vitestでのフロントエンドテストを入れて、最後に ASP.NET CoreのアプリをAzureにデプロイするということをやりたい。
あとは、swaggerでのAPIのMockや、VSCodeThunderClientでAPIのリクエスト投げるとかの記事も書いておきたい。

Hugo で構築されています。
テーマ StackJimmy によって設計されています。