Bot Builder v4 でのテスト : ミドルウェアのテスト

前回はユニットテストの基本を紹介しました。今回は IBot のメイン処理と BotBuilder のミドルウェアをユニットテストしてみます。

# ミドルウェアのテスト

ミドルウェアのテストでは大きく分けて以下の 2 種類があります。

**処理内でアダプター経由の返信があり、それを検証したい場合**
返信はアダプター経由で行われるため、TestAdapter の Use メソッドにテストしたいミドルウェアを渡して、AssetReply で検証を行います。

**処理の結果、次のミドルウェアが実行されるか確認したい場合**
MiddlewareSet を作成して、テストしたいミドルウェアを追加。次のミドルウェアとして任意のコールバックを追加して検証を行います。

今回検証するサンプルボットでは両方のパターンが存在するので、それぞれやり方を紹介します。

# ソリューションの準備

ボットのコードは[Bot Builder v4 でボット開発 : アダプター、TurnContext、Activity](https://qiita.com/kenakamu/items/55c93c044a9ee99bbe22) で開発したものを使います。

1\. GitHub よりレポジトリをクローン。

```shell
git clone https://github.com/kenakamu/botbuilderv4completeguide
cd botbuilderv4completeguide
```
2\. article3 ブランチをチェックアウトして、test-article2 ブランチを作成

```shell
git checkout article3
git checkout -b test-article2
```

3\. myfirstbot フォルダを作成して .git フォルダ、LICENSE、.gitignore 以外を全て移動。
![image.png](https://qiita-image-store.s3.amazonaws.com/0/214116/50467fb9-21b1-9bc5-4c68-1c41249d5498.png)


4\. Visual Studio を起動して新規プロジェクトを作成。空のソリューションを .git フォルダと同じ階層に作成。「ソリューションのディレクトリを作成」のチェックを外す。
![image.png](https://qiita-image-store.s3.amazonaws.com/0/214116/ed45a10c-cf59-9e26-cdaa-62aa6e08aae9.png)
![image.png](https://qiita-image-store.s3.amazonaws.com/0/214116/4b851684-0236-8d5a-6a61-f2e96aa46bbb.png)

5\. ソリューションを右クリックして、既存のプロジェクトを追加。
![image.png](https://qiita-image-store.s3.amazonaws.com/0/214116/abf73750-66f0-ebb4-22f8-bb2fe085961d.png)

6\. myfirstbot プロジェクトを追加。
![image.png](https://qiita-image-store.s3.amazonaws.com/0/214116/3365b0dd-9931-b5da-7e80-a789818b2a5a.png)

7\. ソリューションを右クリックして、新規プロジェクトを追加。.NET Core より MSTest テストプロジェクトを追加。
![image.png](https://qiita-image-store.s3.amazonaws.com/0/214116/bf00fc97-2ef3-890a-ab2b-dba622302f2b.png)

8\. 追加したプロジェクトのプロパティより、ターゲットフレームワークを .NET Core 2.2 を選択。
![image.png](https://qiita-image-store.s3.amazonaws.com/0/214116/d21d6dfe-fce7-bce4-9a5b-d44d390d2fb8.png)

9\. ソリューションを右クリックして「ソリューションの NuGet パッケージの管理」を選択。
![image.png](https://qiita-image-store.s3.amazonaws.com/0/214116/c3562802-922e-5366-b60d-c93347f7f1fc.png)

10\. 以下パッケージをテストプロジェクトにも追加。また myfirstbot プロジェクトをテストプロジェクトに参照追加。

Microsoft.AspNetCore
Microsoft.Bot.Builder.Integration.AspNet.Core
![image.png](https://qiita-image-store.s3.amazonaws.com/0/214116/bdd08670-179d-fc84-2c3a-d722037de54b.png)

11\. Test 関連パッケージの更新があれば最新版に更新。
![image.png](https://qiita-image-store.s3.amazonaws.com/0/214116/04c65f19-2436-a6db-c408-7353b1f28f52.png)

12\. ソリューションをビルドしてエラーがないことを確認。

# テストの実装と実行

今回のボットはミドルウェアが 2 つあり、メインロジックもあるため、テストを複数作成します。テストクラスの分割は対応するクラスに合わせることが多いのですが、今回はシンプルに 1 つのテストクラスに実装してみます。

1\. テストプロジェクトにある既存のテストクラスを MyBotUnitTest.cs に変更。コードを以下のものに差し替え。ここではメインロジックのオウム返しをテスト。

```csharp:MyBotUnitTest.cs
using Microsoft.Bot.Builder.Adapters;
using Microsoft.Bot.Schema;
using Microsoft.VisualStudio.TestTools.UnitTesting;
using System.Collections.Generic;
using System.Threading.Tasks;

namespace myfirstbot.unittest
{
    [TestClass]
    public class MyBotUnitTest
    {
        [TestMethod]
        public async Task MyBot_ShouldReturnSameText()
        {
            // アダプターを作成
            var adapter = new TestAdapter();
            
            // テスト対象のクラスをインスタンス化
            var bot = new MyBot();
            await new TestFlow(adapter, bot.OnTurnAsync)
                .Test("foo", "foo")
                .StartTestAsync();
        }
    }
}
```

2\. 次に MyMiddleware 用のテストを作成。MyBotUnitTest クラスに以下のメソッドを追加。ここでは添付ファイルを送信した場合に、「テキストを送ってください」とメッセージが変えることを検証。

```csharp
[TestMethod]
public async Task MyMiddleware_ShouldStopProcessingWithAttachment()
{            
    // アダプターを作成し、利用するミドルウェアを追加。
    var adapter = new TestAdapter()
        .Use(new MyMiddleware());

    // 添付ファイルを送る
    var activityWithAttachment = new Activity(ActivityTypes.Message)
    {
        Attachments = new List<Attachment>() { new Attachment() }
    };

    // テストの実行
    await new TestFlow(adapter)
    .Send(activityWithAttachment)
    .AssertReply("テキストを送ってください")
    .StartTestAsync();
}
```

3\. 続いて添付ファイルが無い場合を検証するため、以下のメソッドを追加。

- MiddlewareSet に実行したいミドルウェアを追加
- ReceiveActivityWithStatusAsync メソッドでメッセージを送信
- 登録したミドルウェアが全て実行された際に呼び出されるコールバックとして ValidateMiddleware を作成し結果を検証

```csharp
[TestMethod]
public async Task MyMiddleware_ShouldProcessingWithoutAttachment()
{
    var nextMiddlewareCalled = false;

    // 登録したミドルウェがすべて呼ばれた後に呼び出されるコールバック
    Task ValidateMiddleware(ITurnContext turnContext, CancellationToken cancellationToken)
    {
        // 今回は turnContext の中身を検証する必要はないため、
        // 次のミドルウェアが呼び出されたこと自体で検証を成功とする。
        nextMiddlewareCalled = true;
        return Task.CompletedTask;
    }
    // MiddlewareSet にテスト対象のミドルウェアを追加。
    var middlewareSet = new MiddlewareSet();
    middlewareSet.Use(new MyMiddleware());

    // テキストメッセージと ITurnContext を作成。
    var activityWithoutAttachment = new Activity(ActivityTypes.Message)
    {
        Text = "foo"
    };
    var ctx = new TurnContext(new TestAdapter(), activityWithoutAttachment);

    // MiddlewareSet にメッセージを送信。
    await middlewareSet.ReceiveActivityWithStatusAsync(ctx, ValidateMiddleware, default(CancellationToken));

    Assert.IsTrue(nextMiddlewareCalled);
}
```

4\. ソリューションをビルド後、テスト ｜ 実行 ｜すべてのテストをクリックしてテストを実行。
![image.png](https://qiita-image-store.s3.amazonaws.com/0/214116/653d0a6a-2f8e-036c-70a0-5eaf46b478bf.png)

5\. テストエクスプローラーより結果を確認。
![image.png](https://qiita-image-store.s3.amazonaws.com/0/214116/05e797b8-2bae-8040-359e-a4b6fb0fe350.png)

# まとめ

ミドルウェアのテストはその性質により実装を変える必要がありますが、BotBuilder SDK だけで実装できるのは便利です。ミドルウェアとメインロジックのテストはしっかり分離してください。次回はダイアログとステートのユニットテストを見ていきます。

[次の記事へ](https://qiita.com/kenakamu/items/543752e4398d09895e0e)
[目次へ戻る](https://qiita.com/kenakamu/items/6dc043cfc1f199032883)

[この記事のサンプルコード](https://github.com/kenakamu/botbuilderv4completeguide/tree/test-article2)