はじめに
Alexaスキルのテストを自動化するためのテストドライバ ASTD (Alexa Skill Test Driver) を作成したので紹介します。ASTDは、ユーザーとAlexaスキルの対話をシミュレーションして、Alexaスキルの対話テストを自動化するツールです。
ASTDによるテストでは、ユーザーの発話とスキルからの応答をテストデータとして定義します。ASTDはそのテストデータを使ってスキルとの対話を行い、スキルから返された発話内容を検証します。
ASTDはAlexa Skills Kit Skill Management API (SMAPI) を使って、スキルにアクセスします。またテストの実行にはMochaを使っています。
本記事は下記の3部作となっています。
- Alexaスキルのテストを自動化するASTD (前編)
- Alexaスキルのテストを自動化するASTD (後編)
- Alexaスキルのテストを自動化するASTD (リファレンス編)
Hello Worldスキルのテスト
例としてASTDによるHello Worldスキルのテスト結果を下記に示します。Hello Worldスキルは、Amazonの提供するAlexa-Hostedスキルのサンプルスキルです。Alexaスキルの開発者は、誰でも一度は動かしたことがあるのでは無いでしょうか。
U (User) はASTDからスキルへのインプット、A (Alexa) はスキルからのアウトプットになります。ユーザーとスキルの対話フローに沿って、テストが実行されていることが判るかと思います。
$ npm test
> [email protected] test
> mocha --timeout 10000 --slow 3500
Test Hello World
File: test-hello-world
TC: Open Skill and Say Hello
Turn: 1
.............................
... U: Open hello world
... A: Welcome, you can say Hello or Help. Which would you like to try?
.............................
√ Validating Turn 1 (3733ms)
Turn: 2
.............................
... U: hello
... A: Hello World!
.............................
√ Validating Turn 2 (2918ms)
TC: Open Skill and Say Help
Turn: 1
.............................
... U: Open hello world
... A: Welcome, you can say Hello or Help. Which would you like to try?
.............................
√ Validating Turn 1 (2921ms)
Turn: 2
.............................
... U: help
... A: You can say hello to me! How can I help?
.............................
√ Validating Turn 2 (2939ms)
4 passing (13s)
クイックツアー
前述のHello Worldスキルを例として、ASTDを使ったテストの方法について説明します。
セットアップ
まずASTDのセットアップについて説明します。ここでは開発中のスキルの開発環境で、ASTDを使うことを想定しています。ただしスキルコード自体への依存はないので、全く別の環境にセットアップしても構いません。
以下の説明で、ディレクトリ構成などは、お使いの環境に合わせて適宜読み替えてください。
前提環境
下記の環境を必要とします。
またテスト対象として、下記のスキルが作成されているものとします。
- Hello-Worldスキル (Amazonの提供するAlexa-Hostedスキルのサンプルスキル)
ディレクトリ構造
Hello Worldスキルのディレクトリ構造を下図に示します。青字で示されたものは、ASTDを使ったテストのために追加されたディレクトリとファイルです。それぞれについてはこのあとに説明します。テストを行うだけであれば、スキルの実体 (index.jsなど) は、この環境に無くても構いません。
├── lambda/
│ ├── config.json
│ └── test/
│ └── test-hello-world.js
├── skill-package
├── src/
│ └── test/
│ ├── config.json
│ └── test-hello-world.ts
└── tsconfig.json
インストール
ASTDをnpmからインストールします。
cd lambda
npm install alexa-skill-test-driver --save-dev
構成ファイルの用意
ASTDはスキルとSMAPIにアクセスするための構成情報を必要とします。この構成情報を構成ファイル (config.json) に記述する必要があります。
構成ファイルの項目を下記に示します。
{
"skill_id": "",
"client_id": "",
"client_secret": "",
"refresh_token": ""
}
上記のうちclient_id, client_secret, refresh_tokenについては、LWA (Login with Amazon) とASK CLIを使って取得します。詳細は下記を参照してください。
ASK CLIをインストールして構成済みの場合は、これらは実はローカル環境からも取得可能です。
以下にそれぞれの取得先を示します。
項目 | 取得方法 |
---|---|
skill_id | アクセスするスキルのIDです。 開発者コンソールのスキルリストから取得できます。 |
client_id | ~/.ask/authinfoファイルのask_client_idから取得できます。 |
client_secret | ~/.ask/authinfoファイルのask_client_confirmationから取得できます。 |
refresh_token | ~/.ask/cli_configファイルのrefresh_tokenから取得できます。 |
cli_configには複数のプロファイルが定義されている場合があります。テスト対象のスキルの開発に使用しているプロファイルから、上記項目を取得してください。
構成ファイルは下記の場所に作成してください。
TypeScript環境の場合
./src/test/config.json (TypeScriptによって./lambda/test/config.jsonにコピーされます)
JavaScriptの場合
./lambda/test/config.json
TypeScriptのセットアップ
TypeScriptを使用する場合は、tsconfig.jsonに下記の項目を設定してください。
"baseUrl": "./lambda/node_modules",
"rootDir": ".src",
"outDir": "./lambda",
"resolveJsonModule": true
ここではテストデータも./lambdaディレクトリ (実際には./lambda/test) に出力するものとしています。
.gitignoreの更新
ASTDのテストデータはスキルの実行時には必要ありません。.gitignoreで下記のように記述して、テストデータが実行環境にデプロイされないようにします。(Alexa-Hostedスキルの場合は、Gitによってデプロイされるため)
lambda/test/
テストデータの作成
Amazonの提供するHello Worldスキルをテストするテストデータを下記に示します。
- TypeScriptの場合: ./src/test/test-hello-world.ts
- JavaScriptの場合: ./lambda/test/test-hello-world.js
TypeScript版
import * as Astd from 'alexa-skill-test-driver';
import * as config from './config.json';
const testData: Astd.TestData = {
title: 'Test Hello World',
locale: 'en-US',
testCases: [
{
title: 'Open Skill and Say Hello',
turns: [
{
input: {
speak: 'Open hello world',
mode: 'FORCE_NEW_SESSION'
},
output: {
outputSpeech: {
type: 'SSML',
ssml: 'Welcome, you can say Hello or Help. Which would you like to try?'
},
reprompt: {
type: 'SSML',
ssml: 'Welcome, you can say Hello or Help. Which would you like to try?'
},
shouldEndSession: false
}
},
{
input: {
speak: 'hello'
},
output: {
outputSpeech: {
type: 'SSML',
ssml: 'Hello World!'
}
}
}
]
},
{
title: 'Open Skill and Say Help',
turns: [
{
input: {
speak: 'Open hello world',
mode: 'FORCE_NEW_SESSION'
},
output: {
outputSpeech: {
type: 'SSML',
ssml: 'Welcome, you can say Hello or Help. Which would you like to try?'
},
reprompt: {
type: 'SSML',
ssml: 'Welcome, you can say Hello or Help. Which would you like to try?'
},
shouldEndSession: false
}
},
{
input: {
speak: 'help'
},
output: {
outputSpeech: {
type: 'SSML',
ssml: 'You can say hello to me! How can I help?'
},
reprompt: {
type: 'SSML',
ssml: 'You can say hello to me! How can I help?'
},
shouldEndSession: false
}
}
]
}
]
};
(async () => await Astd.executeTestGroup(testData, config))();
Javascript版
const Astd = require('alexa-skill-test-driver');
const config = require('./config.json');
const testData = {
title: 'Test Hello World',
locale: 'en-US',
testCases: [
{
title: 'Open Skill and Say Hello',
turns: [
{
input: {
speak: 'Open hello world',
mode: 'FORCE_NEW_SESSION'
},
output: {
outputSpeech: {
type: 'SSML',
ssml: 'Welcome, you can say Hello or Help. Which would you like to try?'
},
reprompt: {
type: 'SSML',
ssml: 'Welcome, you can say Hello or Help. Which would you like to try?'
},
shouldEndSession: false
}
},
{
input: {
speak: 'hello'
},
output: {
outputSpeech: {
type: 'SSML',
ssml: 'Hello World!'
}
}
}
]
},
{
title: 'Open Skill and Say Help',
turns: [
{
input: {
speak: 'Open hello world',
mode: 'FORCE_NEW_SESSION'
},
output: {
outputSpeech: {
type: 'SSML',
ssml: 'Welcome, you can say Hello or Help. Which would you like to try?'
},
reprompt: {
type: 'SSML',
ssml: 'Welcome, you can say Hello or Help. Which would you like to try?'
},
shouldEndSession: false
}
},
{
input: {
speak: 'help'
},
output: {
outputSpeech: {
type: 'SSML',
ssml: 'You can say hello to me! How can I help?'
},
reprompt: {
type: 'SSML',
ssml: 'You can say hello to me! How can I help?'
},
shouldEndSession: false
}
}
]
}
]
};
(async () => await Astd.executeTestGroup(testData, config))();
Hello Worldスキルの受け付ける対話パターンに沿って、下記の2つのテストケースが定義されています。
- “Open Skill and Say Hello”
- “Open Skill and Say Help”
TypeScript版は、コンパイルが必要です。
tsc
GitHubにテストデータを置いています。
テストの実行
まずpackage.jsonに下記を追加します。
scripts: {
"test": "mocha --timeout 15000"
}
mochaのデフォルトのタイムアウトは2,000ms (2秒) ですが、Alexaとの対話は通常それ以上の時間がかかります。ここでは15,000ms (15秒) を指定しています。
次にコマンドラインからnpmを使って実行します。
cd lambda
npm test
実行結果は下記のようになります。
$ npm test
> [email protected] test
> mocha --timeout 10000 --slow 3500
Test Hello World
File: test-hello-world
TC: Open Skill and Say Hello
Turn: 1
.............................
... U: Open hello world
... A: Welcome, you can say Hello or Help. Which would you like to try?
.............................
√ Validating Turn 1 (3733ms)
Turn: 2
.............................
... U: hello
... A: Hello World!
.............................
√ Validating Turn 2 (2918ms)
TC: Open Skill and Say Help
Turn: 1
.............................
... U: Open hello world
... A: Welcome, you can say Hello or Help. Which would you like to try?
.............................
√ Validating Turn 1 (2921ms)
Turn: 2
.............................
... U: help
... A: You can say hello to me! How can I help?
.............................
√ Validating Turn 2 (2939ms)
4 passing (13s)
U (User) は、テストデータに定義された発話データ、A (Alexa) はAlexaから返ってきた応答です。TC (Test Case) ごとに、マルチターンの対話が行われていることが判ります。
エラーが発生した場合は下記のようになります。ここではスキルから”Welcome, you can …”と返すところを、”Welcome! you can …”と誤ったレスポンスを返しています。2つのTCともにターン1でエラーが検出され、ターン2の実行はスキップしています。
ログの後半にエラーの内容が出力されています。expectedはテストデータに定義されていた発話内容、actualは実際にスキルから出力された発話内容を示しています。これによりどこを修正すべきかを確認することができます。
$ npm test
> [email protected] test
> mocha --timeout 15000 --slow 3000
Test Hello World
File: test-hello-world
TC: Open Skill and Say Hello
Turn: 1
.............................
... U: Open hello world
... A: Welcome! you can say Hello or Help. Which would you like to try?
.............................
1) Validating Turn 1
Turn: 2
- Validating Turn 2
TC: Open Skill and Say Help
Turn: 1
.............................
... U: Open hello world
... A: Welcome! you can say Hello or Help. Which would you like to try?
.............................
2) Validating Turn 1
Turn: 2
- Validating Turn 2
0 passing (11s)
2 pending
2 failing
1) Test Hello World
File: test-hello-world
TC: Open Skill and Say Hello
Turn: 1
Validating Turn 1:
Unmatched OutputSpeech
+ expected - actual
{
- "ssml": "<speak>Welcome! you can say Hello or Help. Which would you like to try?</speak>"
+ "ssml": "<speak>Welcome, you can say Hello or Help. Which would you like to try?</speak>"
"type": "SSML"
}
at Context.<anonymous> (node_modules\alexa-skill-test-driver\dist\index.js:360:31)
at processTicksAndRejections (internal/process/task_queues.js:93:5)
2) Test Hello World
File: test-hello-world
TC: Open Skill and Say Help
Turn: 1
Validating Turn 1:
Unmatched OutputSpeech
+ expected - actual
{
- "ssml": "<speak>Welcome! you can say Hello or Help. Which would you like to try?</speak>"
+ "ssml": "<speak>Welcome, you can say Hello or Help. Which would you like to try?</speak>"
"type": "SSML"
}
at Context.<anonymous> (node_modules\alexa-skill-test-driver\dist\index.js:360:31)
at processTicksAndRejections (internal/process/task_queues.js:93:5)
以上でHello Worldスキルの、ASTDによるテスト方法について簡単に紹介しました。前編はここまでです。後編ではテストデータの定義内容について説明します。
Alexaスキルのテストを自動化するASTD (後編) につづく
変更履歴
日付 | 内容 |
---|---|
2021/09/06 | 前提環境にMochaを追加 |
2021/08/31 | 加筆・改良 |
2021/06/19 | 記述内容の補足と改良 |
2021/06/10 | 初版リリース |