Bot Framework と Microsoft Graph で DevOps その 1 : ユニットテスト概要

Bot 開発してますか?DevOps 取り入れてますか?ということで今回からしばらくこのテーマに沿って考えてみます。
具体的に Microsoft Graph を利用したアプリを作りながら作業を進めますが、初回の今回はテストの概念をまず。

- DevOps についてはメソッド屋のブログにある 「DevOps スタータキット」を参考にしてください。
- Bot Framework を利用したボットアプリ開発は多く記事があるので検索してみてください。例えばSecretaryBot 開発チーム Blog などは分かりやすいです。

Bot Framework とユニットテスト概要

DevOps で一番大事なことはカルチャーですが、技術的な面に限定すると、個人的にはテストです。テストの自動化ができないと継続デリバリーできません。Bot Framework で作るボットアプリの場合テストは UI が無い分簡単ですが、MVC 等他のアプリとは異なるアプローチが必要です。以下ブログはユニットテストにおいてその点の考察がよくまとまっています。翻訳は機械に任せて理解すべきところを意訳してみます。かなり抜粋して意訳しているので原文も是非。

参考元 https://www.microsoft.com/reallifecode/2017/01/20/unit-testing-for-bot-applications/
Thanks to Catalyst team and Mor Shemesh who shared their precious knowledge and allow me to re-use your contents.

ちなみにブログは Node.js での例ですが、私は C# メインなので、後半で C# で詳細説明します。

何をテストすべきか

Bot Framework でダイアログやインテントを利用した開発を行う場合、フレームワークに強く依存します。またステートサービスや LUIS などは外部接続が必要です。通常のテストはインプットをメソッドに渡して、アウトプットを検証しますが、ロジックが外部依存する場合、依存サービスを抽象化する必要があります。この場合以下の方法が考えられます。

  • 自分のコードを別のプロジェクトに移動して、Bot Framework の依存を削除する。
  • 外部サービスに依存せずにテストする方法を検討する。

コード分離は困難

Web アプリ開発などと異なり、ボットアプリの開発は会話フローを表現するため、コードの書き方が異なります。例えば以下のフローを考えます。

  • ユーザーに選択肢を出す
  • ユーザーの回答から、新しい会話を送ったり、タイピング中などの情報を送る
  • 上記の繰り返し
  • 会話を終了する

上記処理はそれぞれメソッドとして記述され、ユーザーとのやり取りは複数の REST コールと応答となります。これらの会話を分離するとテストできない箇所が増えるし、会話全体のテストができません。ボットアプリでユニットテストを考えた場合、会話フローのロジックこそがテストすべきテストです。

どうやって会話フローをテストするか

Node.js を利用している場合、ConsoleConnector を使うことで動作をシミュレートできます。C# の場合は Bot Builder GitHub Repo にあるテストコードを利用することで同じことが出来ます。雰囲気をつかんでもらうために以下に Node.js のサンプルコードを示します。

 var step = 1;
bot.on('send', function (message) {
  
  switch(step++) {
    case 1:
      assert(message.text == 'What would you like to do?');
      connector.processMessage('Play Games');
      break;

    case 2:
      assert(message.text == 'When?');
      connector.processMessage('Always');
      break;

    case 3:
      assert(message.text == 'Why?');
      connector.processMessage('I'm too cool for school');
      break;

    case 4:
      assert(message.text == 'No problem');
      break;

    default:
      assert(false); // The conversation should have ended
  }
  
});
connector.processMessage('Hello World');

これを見てわかるようにすべてのステップを通しでテストしています。

LUIS のテスト

外部サービである LUIS をテストする場合、Nock や Moq などで動作をシミュレートすることで依存を除外します。つまり実際に LUIS を実行するのではなく、期待した値を常に返すようにサービスをモックすることで対応します。

Node.js でのテスト

ブログにある通り、サンプルがあるので、そちらを活用してください。Node.js 普段使わないので詳細な説明は割愛します。

ユニットテストのサンプル
https://github.com/CatalystCode/alarm-bot-unit-testing
実際のアプリでの利用事例
https://github.com/CatalystCode/multilingual-uber-bot

C# でのテスト

C# でのテストについては、次回具体的に紹介します。

まとめ

ボットアプリのユニットテストをする際は、メソッド単位ではなく、会話フロー単位でテストするのが良いということでした。賛否はあるでしょうが、私はこの方法を支持します。

さて、次回から実際にコードを書きますが以下環境を使います。

- Visual Studio 2017 (ダウンロード)
- Visual Studio Team Services (トライアル)
- Bot Framework テンプレート (ダウンロード)
- Bot Framework エミュレーター (ダウンロード)
- Office 365 (トライアル)