kun432's blog

Alexaなどスマートスピーカーの話題中心に、Voiceflowの日本語情報を発信してます。たまにAWSやkubernetesなど。

〜スマートスピーカーやVoiceflowの記事は右メニューのカテゴリからどうぞ。〜

AlexaスキルのテストをBotiumでやってみた②

f:id:kun432:20210131175240p:plain

前回の続き。

テストシナリオの書き方について。

目次

前回のテストシナリオ

前回用意したテストシナリオを一つピックアップします。

tripadvisor_happypath01.convo.txt

トリップアドバイザーのテスト:Happy Path #1

#me
トリップアドバイザーを開いて

#bot
はじめまして、トリップアドバイザースキルをご利用いただきありがとうございます。このスキルでは行きたい日本の都市名をいうとおすすめの観光名所を提案します。例えば「京都に行きたい」と言ってみてください。

#me
京都 かな

#bot
京都 ですね。京都 は清水寺がおすすめです。

会話の内容がそのまま書いてあり、ユーザの発話パターンに応じて別のシナリオにしています。これはこれでいいんですが、色々バリエーションが増えてくると難しい。ということでこれを分離していきましょう。

発話のバリエーション

発話のバリエーションに対応するのがutterances.txtになります。以下のようなファイルを用意します。

happypath01_utt.utterances.txt

HAPPYPATH01_UTT
京都
京都 かな
京都 がいいな
京都 に行きたい

convo.txtの方を修正します。

トリップアドバイザーのテスト:Happy Path #1

#me
トリップアドバイザーを開いて

#bot
はじめまして、トリップアドバイザースキルをご利用いただきありがとうございます。このスキルでは行きたい日本の都市名をいうとおすすめの観光名所を提案します。例えば「京都に行きたい」と言ってみてください。

#me
HAPPYPATH01_UTT

#bot
京都 ですね。京都 は清水寺がおすすめです。

では実行してみましょう。他のテストファイルと分けるためにディレクトリを切って移動します。

$ mkdir happypath01
$ mv tripadvisor_happypath01.convo.txt happypath01/.
$ mv happypath01_utt.utterances.txt happypath01/.

ディレクトリを指定するには--convosを使います。

$ botium-cli run --convos happypath01


  Botium Test-Suite
    ✔ トリップアドバイザーのテスト:Happy Path #1/HAPPYPATH01_UTT-L1 (10810ms)
    ✔ トリップアドバイザーのテスト:Happy Path #1/HAPPYPATH01_UTT-L2 (10065ms)
    ✔ トリップアドバイザーのテスト:Happy Path #1/HAPPYPATH01_UTT-L3 (10429ms)
    ✔ トリップアドバイザーのテスト:Happy Path #1/HAPPYPATH01_UTT-L4 (10022ms)


  4 passing (42s)

utterances.txtに記載した4パターンでテストされているようですね。--verboseをつけてみるとよくわかります。

$ botium-cli run --convos happypath01 --verbose
(...snip...)
  botium-core-ScriptingProvider  HAPPYPATH01_UTT ({ convoDir: 'happypath01', filename: 'happypath01_utt.utterances.txt' }): 京都|京都 かな|京都 がいいな|京都 に行きたい +0ms
(...snip...)
  botium-core-ScriptingProvider assertBotResponse トリップアドバイザーのテスト:Happy Path #1/HAPPYPATH01_UTT-L1/Line 12 (Line 9: #me - 京都) BOT: 京都 ですね。京都 は清水寺がおすすめです。 = 京都 ですね。京都 は清水寺がおすすめです。 ... +5s
  botium-cli-run トリップアドバイザーのテスト:Happy Path #1/HAPPYPATH01_UTT-L1 ready, calling done function. +11s
(...snip...)
  botium-core-ScriptingProvider assertBotResponse トリップアドバイザーのテスト:Happy Path #1/HAPPYPATH01_UTT-L2/Line 12 (Line 9: #me - 京都 かな) BOT: 京都 ですね。京都 は清水寺がおすすめです。 = 京都 ですね。京都 は清水寺がおすすめです。 ... +5s
  botium-cli-run トリップアドバイザーのテスト:Happy Path #1/HAPPYPATH01_UTT-L2 ready, calling done function. +10s
(...snip...)
  botium-core-ScriptingProvider assertBotResponse トリップアドバイザーのテスト:Happy Path #1/HAPPYPATH01_UTT-L3/Line 12 (Line 9: #me - 京都 がいいな) BOT: 京都 ですね。京都 は清水寺がおすすめです。 = 京都 ですね。京都 は清水寺がおすすめです。 ... +5s
  botium-cli-run トリップアドバイザーのテスト:Happy Path #1/HAPPYPATH01_UTT-L3 ready, calling done function. +8s
(...snip...)
  botium-core-ScriptingProvider assertBotResponse トリップアドバイザーのテスト:Happy Path #1/HAPPYPATH01_UTT-L4/Line 12 (Line 9: #me - 京都 に行きたい) BOT: 京都 ですね。京都 は清水寺がおすすめです。 = 京都 ですね。京都 は清水寺がおすすめです。 ... +5s
  botium-cli-run トリップアドバイザーのテスト:Happy Path #1/HAPPYPATH01_UTT-L4 ready, calling done function. +11s
(...snip...)

  4 passing (41s)

もちろんAlexa(というかボット側)からの発話にバリエーションがある場合もutterances.txtは使えるようです。

スロットを変数として扱う

前回作成したもう一つのテストシナリオ見てみましょう。tripadvisor_happypath02.convo.txtのほうです。

トリップアドバイザーのテスト:Happy Path #2

#me
トリップアドバイザーを開いて

#bot
はじめまして、トリップアドバイザースキルをご利用いただきありがとうございます。このスキルでは行きたい日本の都市名をいうとおすすめの観光名所を提案します。例えば「京都に行きたい」と言ってみてください。

#me
奈良 に行きたい

#bot
奈良 ですね。奈良 は東大寺がおすすめです。

ユーザが「奈良」という可変となる値、すなわち「スロット」を発話したのに対して、ボットも繰り返すように「奈良」と答えていますよね。こういう場合には変数が使えます。Botiumでは"Scripting Memory"と呼ばれる機能です。

Scripting Memoryは初期状態では有効化されていないので、まずこれを有効化しましょう。botium.jsonを開いて、botium.Capabilitiesに以下を追加します。

{
  "botium": {
    "Capabilities": {
(...snip...)
      "SCRIPTING_ENABLE_MEMORY": true
    }
  }
}

ではテストシナリオを修正していきます。最初と同じようにディレクトリを作成してconbo.txtを移動しておきましょう。

$ mkdir happypath02
$ mv tripadvisor_happypath02.convo.txt happypath02/.

tripadvisor_happypath02.convo.txtを以下のように書き換えます。

トリップアドバイザーのテスト:Happy Path #2

#begin
SET_SCRIPTING_MEMORY city|奈良

#me
トリップアドバイザーを開いて

#bot
はじめまして、トリップアドバイザースキルをご利用いただきありがとうございます。このスキルでは行きたい日本の都市名をいうとおすすめの観光名所を提案します。例えば「京都に行
きたい」と言ってみてください。

#me
$city に行きたい

#bot
$city ですね。$city は東大寺がおすすめです。

cityという変数を用意して、最初にSET_SCRIPTING_MEMORYで「奈良」を設定しています。あとのやりとりではその部分はすべて$cityで記載しています。

実行してみます。

$ botium-cli run --convos happypath02 --verbose
(...snip...)
  botium-core-SetScriptingMemoryLogicHook Set scripting memory variable "$city" from "undefined" to "奈良, isGlobal: false, type: onConvoBegin" +0ms
(...snip...)
  botium-core-Convo トリップアドバイザーのテスト:Happy Path #2/Line 12: user says (cleaned by binary and base64 data and sourceData) {
  botium-core-Convo   "sender": "me",
  botium-core-Convo   "channel": null,
  botium-core-Convo   "not": false,
  botium-core-Convo   "optional": false,
  botium-core-Convo   "messageText": "奈良 に行きたい",
  botium-core-Convo   "media": null,
  botium-core-Convo   "buttons": null,
  botium-core-Convo   "cards": null,
  botium-core-Convo   "forms": null,
  botium-core-Convo   "attachments": null,
  botium-core-Convo   "asserters": [],
  botium-core-Convo   "userInputs": [],
  botium-core-Convo   "logicHooks": []
  botium-core-Convo } +2ms
(...snip...)
  botium-core-ScriptingMemory fill start: {} +5s
  botium-core-ScriptingMemory fill end: { '$city': '奈良' } +1ms
  botium-core-ScriptingProvider assertBotResponse トリップアドバイザーのテスト:Happy Path #2/Line 15 (Line 12: #me - $city に行きたい) BOT: 奈良 ですね。奈良 は東大寺がおすすめです。 = 奈良 ですね。奈良 は東大寺がおすすめです。 ... +5s
(...snip...)

  1 passing (12s)

対話のやり取りで$cityに当たる部分が、最初に設定した「奈良」で置き換えられて会話が行われているのがわかりますでしょうか。これを使えば、スロットのような可変の値もテストができますね。

上記ではconbo.txtに直接記載して変数を設定しています。

#begin
SET_SCRIPTING_MEMORY city|奈良

シナリオごとにこれを書くのはちょっと意味がないですよね。そして、ユーザの発話に応じておすすめされる「清水寺」や「東大寺」の部分も可変なので変数にしたいですね。ではこれを別のファイルに切り出して、happypath01/02のテストを一つにしてしまいましょう。

新しくディレクトリを用意して、conbo.txtをこんな感じで作ります。

$ mkdir happypath
$ vi happypath/tripadvisor_happypath.conbo.txt
トリップアドバイザーのテスト:Happy Path

#me
トリップアドバイザーを開いて

#bot
はじめまして、トリップアドバイザースキルをご利用いただきありがとうございます。このスキルでは行きたい日本の都市名をいうとおすすめの観光名所を提案します。例えば「京都に行きたい」と言ってみてください。

#me
$city に行きたい

#bot
$city ですね。$city は$placeがおすすめです。

以下の内容でhappypath/tripadvisor_happypath.scriptingmemory.txt を作成します。

      |$city |$place
Case1 |京都  |清水寺
Case2 |奈良  |東大寺

実行してみましょう。scriptingmemory.txtを使う場合は--expandscriptingmemoryが必要なようです。

$ botium-cli run --convos happypath --expandscriptingmemory true --verbose
(...snip...)
  botium-core-ScriptingProvider ReadConvosFromDirectory(happypath) scripting memories:
  botium-core-ScriptingProvider  {
  botium-core-ScriptingProvider   header: { name: 'Case1' },
  botium-core-ScriptingProvider   values: { '$city': '京都', '$place': '清水寺' },
  botium-core-ScriptingProvider   sourceTag: { filename: 'tripadvisor_happypath.scriptingmemory.txt' }
  botium-core-ScriptingProvider }
  botium-core-ScriptingProvider {
  botium-core-ScriptingProvider   header: { name: 'Case2' },
  botium-core-ScriptingProvider   values: { '$city': '奈良', '$place': '東大寺' },
  botium-core-ScriptingProvider   sourceTag: { filename: 'tripadvisor_happypath.scriptingmemory.txt' }
(...snip...)
  botium-core-Convo トリップアドバイザーのテスト:Happy Path.Case1/Line 9: user says (cleaned by binary and base64 data and sourceData) {
  botium-core-Convo   "sender": "me",
  botium-core-Convo   "channel": null,
  botium-core-Convo   "not": false,
  botium-core-Convo   "optional": false,
  botium-core-Convo   "messageText": "京都 に行きたい",
  botium-core-Convo   "media": null,
  botium-core-Convo   "buttons": null,
  botium-core-Convo   "cards": null,
  botium-core-Convo   "forms": null,
  botium-core-Convo   "attachments": null,
  botium-core-Convo   "asserters": [],
  botium-core-Convo   "userInputs": [],
  botium-core-Convo   "logicHooks": []
  botium-core-Convo } +2ms
(...snip...)
  botium-core-ScriptingMemory fill start: {} +5s
  botium-core-ScriptingMemory fill end: { '$city': '京都', '$place': '清水寺' } +0ms
  botium-core-ScriptingProvider assertBotResponse トリップアドバイザーのテスト:Happy Path.Case1/Line 12 (Line 9: #me - $city に行きたい) BOT: 京都 ですね。京都 は清水寺がおすすめです。 = 京都 ですね。京都 は清水寺がおすすめです。 ... +5s
  botium-cli-run トリップアドバイザーのテスト:Happy Path.Case1 ready, calling done function. +10s
    ✔ トリップアドバイザーのテスト:Happy Path.Case1 (10032ms)
(...snip...)
  botium-core-Convo トリップアドバイザーのテスト:Happy Path.Case2/Line 9: user says (cleaned by binary and base64 data and sourceData) {
  botium-core-Convo   "sender": "me",
  botium-core-Convo   "channel": null,
  botium-core-Convo   "not": false,
  botium-core-Convo   "optional": false,
  botium-core-Convo   "messageText": "奈良 に行きたい",
  botium-core-Convo   "media": null,
  botium-core-Convo   "buttons": null,
  botium-core-Convo   "cards": null,
  botium-core-Convo   "forms": null,
  botium-core-Convo   "attachments": null,
  botium-core-Convo   "asserters": [],
  botium-core-Convo   "userInputs": [],
  botium-core-Convo   "logicHooks": []
  botium-core-Convo } +1ms
(...snip...)
  botium-core-ScriptingMemory fill start: {} +5s
  botium-core-ScriptingMemory fill end: { '$city': '奈良', '$place': '東大寺' } +1ms
  botium-core-ScriptingProvider assertBotResponse トリップアドバイザーのテスト:Happy Path.Case2/Line 12 (Line 9: #me - $city に行きたい) BOT: 奈良 ですね。奈良 は東大寺がおすすめです。 = 奈良 ですね。奈良 は東大寺がおすすめです。 ... +5s
  botium-cli-run トリップアドバイザーのテスト:Happy Path.Case2 ready, calling done function. +11s
    ✔ トリップアドバイザーのテスト:Happy Path.Case2 (10646ms)

  2 passing (22s)

はい、それぞれが展開されて2回テストが行われているのがわかりました。

まとめ

まだまだ触りの部分しか触れていませんが、他にも

  • テストシナリオとなるconvoは、上記の書き方以外にもExcel、CSV、YAML、JSON、Markdownなどでも書ける。
  • シナリオの分割(シナリオから別のシナリオを呼び出す)もできる。
  • Scripting Memoryでは関数も使える。
  • マルチモーダルでのボタンアクションなどにも対応できる

などなど機能はかなり豊富にあるようですので、柔軟なテストシナリオを書くことができそうです。興味があればドキュメントをご覧ください。

botium-docs.readthedocs.io

次回はちょっとBotium Boxを見てみたいと思います。