.NET 6 の Azure Functions で xUnit を使ってみた

.NET の Azure Functions でバックエンドアプリケーションを開発しています。公式ドキュメント通りに進めると、dotnet コマンドは使わず、func コマンドで開発を進めることになります。とはいえ、func コマンドの裏ではおそらく dotnet build しているだろうから、dotnet test もできるはず。ということで、xUnit を使ってユニットテスト（単体テスト）をやってみました。

## Azure Functions と xUnit プロジェクトをまとめる .NET ソリューションを作成

```bash:bash
dotnet new sln -o sampleapp

cd sampleapp
```

## Azure Functions プロジェクトを作成

```bash:bash
func init sampleapp --dotnet

cd sampleapp

func new --name Sample --template HttpTrigger

func start
```


## .NET ソリューションに Azure Functions プロジェクトを

```bash:bash
cd ..

dotnet sln add sampleapp/sampleapp.csproj

dotnet build
```

## xUnit プロジェクトを作成

```bash:bash
dotnet new xunit -o sampleapp.tests

dotnet add sampleapp.tests/sampleapp.tests.csproj \
  reference sampleapp/sampleapp.csproj

dotnet sln add sampleapp.tests/sampleapp.tests.csproj

dotnet test
```

```txt
VSTest のバージョン 17.11.1 (arm64)

テスト実行を開始しています。お待ちください...
合計 1 個のテスト ファイルが指定されたパターンと一致しました。

成功!   -失敗:     0、合格:     1、スキップ:     0、合計:     1、期間: < 1 ms - sampleapp.tests.dll (net8.0)
```

## テストコードを変更

```bash:bash
cd sampleapp.tests

dotnet add package Moq

code UnitTest1.cs
```

```cs:UnitTest1.cs
using System.Threading.Tasks;
using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Mvc;
using Microsoft.Extensions.Logging;
using Xunit;
using Moq;

namespace sampleapp.tests;

public class UnitTest1
{
    [Fact]
    public async Task Run_ReturnsOkResult_WhenNameIsNonProvided()
    {
        var mockLogger = new Mock<ILogger>();
        var httpContext = new DefaultHttpContext();
        httpContext.Request.QueryString = new QueryString();
        var request = httpContext.Request;
        var result = await sampleapp.Sample.Run(request, mockLogger.Object);

        var okResult = Assert.IsType<OkObjectResult>(result);
        Assert.Equal("This HTTP triggered function executed successfully. Pass a name in the query string or in the request body for a personalized response.", okResult.Value);
    }

    [Fact]
    public async Task Run_ReturnsOkResult_WhenNameIsProvided()
    {
        var mockLogger = new Mock<ILogger>();
        var httpContext = new DefaultHttpContext();
        httpContext.Request.QueryString = new QueryString("?name=John");
        var request = httpContext.Request;
        var result = await sampleapp.Sample.Run(request, mockLogger.Object);

        var okResult = Assert.IsType<OkObjectResult>(result);
        Assert.Equal("Hello, John. This HTTP triggered function executed successfully.", okResult.Value);
    }
}
```

## ユニットテスト実行

```bash:bash
dotnet test
```

```txt
VSTest のバージョン 17.11.1 (arm64)

テスト実行を開始しています。お待ちください...
合計 1 個のテスト ファイルが指定されたパターンと一致しました。

成功!   -失敗:     0、合格:     2、スキップ:     0、合計:     2、期間: 2 ms - sampleapp.tests.dll (net8.0)
```

```bash:bash
cd ..

dotnet test
```

```txt
VSTest のバージョン 17.11.1 (arm64)

テスト実行を開始しています。お待ちください...
合計 1 個のテスト ファイルが指定されたパターンと一致しました。

成功!   -失敗:     0、合格:     2、スキップ:     0、合計:     2、期間: 1 ms - sampleapp.tests.dll (net8.0)
```

## 参考

https://learn.microsoft.com/ja-jp/dotnet/core/testing/unit-testing-with-dotnet-test

https://learn.microsoft.com/ja-jp/azure/devops/pipelines/ecosystems/dotnet-core?view=azure-devops&tabs=yaml-editor

https://learn.microsoft.com/ja-jp/dotnet/core/testing/unit-testing-best-practices