ツナ缶雑記

ぐうたらSEのブログです。主にマイクロソフト系技術を中心に扱います。

Azure Pipelines で SQL Server LocalDB を用いたテストを実行する

f:id:masatsuna:20211204142845p:plain

Azure Pipelines では、ビルドサーバーを大きく 2 つの種類から選択できます。 1 つ目は「MS-Hosted Agent」と呼ばれるもので、雑に言うとマイクロソフトさんが準備したビルドマシンをオンデマンドで借用する方式です。 2 つ目は「Self Hosted Agent」と呼ばれるもので、自分で用意したマシンをビルドマシンとして登録して利用する方式です。

MS-Hosted Agent は、特に自分で何か用意する必要はなく、非常にお手軽に利用できます。 マイクロソフトさんが提供しているエージェントの中から、利用したいものを選択する形式です。 そのため、万人が必要とするソフトウェア以外、エージェントにはインストールされていません。

今回は MS-Hosted Agent で SQL Server LocalDB を用いたテストを実行する方法について解説します。

環境

  • SQL Server LocalDB(特にバージョン問わず)
  • .NET 6
  • Visual Studio 2022
  • Entity Framework Core 6.0.0
  • xUnit 2.4.1
  • Azure Pipelines ( MS-Hosted Agent windows-2022)

サンプルコード

本稿でご紹介するサンプルコードは、以下から全量を参照できます。 記事内では重要な部分のみ抜粋して掲載しますので、適宜参照してください。

github.com

Agent の SQL Server LocalDB を起動する

実は MS-Hosted Agent の windows-2022 マシンには、 SQL Server LocalDB がちゃんとインストールされています。 ただ、起動していない状態になっています。 Azure Pipelines の YAML ファイル内で、 SQL Server LocalDB を起動するスクリプトを実行しましょう。

- task: CmdLine@2
  displayName: 'SQL Server LocalDB の起動'
  inputs:
    script: |
      sqllocaldb start mssqllocaldb

これで SQL Server LocalDB が起動します。

データベースを構築する

SQL Server LocalDB を起動しただけでは、サーバーは空の状態です。 ここにテスト用のデータベースを構築していきます。 今回はプロジェクトに含めてある Entity Framework Core のマイグレーションを実行して、データベースを構築してみます。

Entity Framework Core のマイグレーションを実行するためには、 Entity Framework Core ツールをインストールしないといけません。 今回の例では DotNetCoreCLI@2 のタスクを使ってインストールしています。

インストールしたら、同じく DotNetCoreCLI@2 のタスクを使ってマイグレーションを適用します。

- task: DotNetCoreCLI@2
  displayName: 'dotnet-ef のインストール'
  inputs:
    command: 'custom'
    custom: 'tool'
    arguments: 'install --global dotnet-ef'
- task: DotNetCoreCLI@2
  displayName: 'テスト用データベースの構築'
  inputs:
    command: 'custom'
    custom: 'ef'
    arguments: 'database update'
    workingDirectory: '$(Build.SourcesDirectory)/TestWithLocaldb/src/TestWithLocaldb.DataAccess/'

これでデータベースの構築ができます。

今回は Entity Framework Core のマイグレーションを用いてデータベースを構築しましたが、 SQL ファイルを実行してもデータベースを構築できます。 その場合は以下のように、 SQL Server LocalDB を起動した直後に sqlcmd をたたいて構築するのが良いと思います。

- task: CmdLine@2
  displayName: 'SQL Serverセットアップ'
  inputs:
    script: |
      sqllocaldb start mssqllocaldb
      sqlcmd -S (localdb)\MSSQLLocalDB -E -i $(Build.SourcesDirectory)/SqlServerSample/sqls/InitializeDatabase.sql

※このコードは、サンプルコードの GitHub リポジトリに含まれていません。

テストの実行

データベースの構築が終わったら、テストを行うタスクを走らせましょう。

- task: DotNetCoreCLI@2
  displayName: '単体テストの実行'
  inputs:
    command: 'test'
    projects: '$(TestProjects)'
    arguments: '--configuration $(BuildConfiguration) --collect "XPlat Code coverage" -- RunConfiguration.DisableAppDomain=true'  

ビルドパイプラインを実行してみる

今回はちゃんとデータベースを使ったテストができるか確認することを目的にして、簡易な確認を行ってみます。 データベースにデータをくべて、その値を取得するというあまり意味のないテストです。

namespace TestWithLocaldb.DataAccess.Test;

public class UnitTest
{
    [Fact]
    public void データベースを利用したテスト()
    {
        InitializeDatabase();

        using var dbContext = new ProductDbContext();
        var products = dbContext.Products
            .Where(p => p.Publisher == "秋田")
            .OrderBy(p => p.Id)
            .ToList();
        Assert.Collection(products,
            product =>
            {
                Assert.Equal(2, product.Id);
                Assert.Equal("きりたんぽ", product.Name);
            },
            product =>
            {
                Assert.Equal(3, product.Id);
                Assert.Equal("なまはげ", product.Name);
            });
    }

    private static void InitializeDatabase()
    {
        var connectionString = "Data Source=(localdb)\\MSSQLLocalDB;Initial Catalog=sample-database;Integrated Security=True;";
        using var connection = new SqlConnection(connectionString);
        using var deleteCommand = connection.CreateCommand();
        deleteCommand.CommandText = "DELETE FROM [dbo].[Products];";
        using var insertCommand = connection.CreateCommand();
        insertCommand.CommandText = "INSERT [dbo].[Products] ([Id], [Name], [Publisher]) VALUES (1, N'りんご', N'青森');" +
            "INSERT [dbo].[Products] ([Id], [Name], [Publisher]) VALUES (2, N'きりたんぽ', N'秋田');" +
            "INSERT [dbo].[Products] ([Id], [Name], [Publisher]) VALUES (3, N'なまはげ', N'秋田');";
        connection.Open();
        deleteCommand.ExecuteNonQuery();
        insertCommand.ExecuteNonQuery();
    }
}

このテストを Azure Pipelines 上で実行してみると、ちゃんと実行できていることが確認できます。

f:id:masatsuna:20211204141300p:plain

まとめ

今回は MS-Hosted Agent を使って、 SQL Server LocalDB に依存したテストを実行してみました。 データベースに依存したテストを大量に実行するのはあまりおすすめできませんが、限られたテストケースを実行するのであれば、十分実用に耐えられるのではないでしょうか。