Alexaスキルのテストを自動化するASTD (前編)

はじめに

Alexaスキルのテストを自動化するためのテストドライバ ASTD (Alexa Skill Test Driver) を作成したので紹介します。ASTDは、ユーザーとAlexaスキルの対話をシミュレーションして、Alexaスキルの対話テストを自動化するツールです。

ASTDによるテストでは、ユーザーの発話とスキルからの応答をテストデータとして定義します。ASTDはそのテストデータを使ってスキルとの対話を行い、スキルから返された発話内容を検証します。

ASTDはAlexa Skills Kit Skill Management API (SMAPI) を使って、スキルにアクセスします。またテストの実行にはMochaを使っています。

本記事は下記の3部作となっています。

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を使うことを想定しています。ただしスキルコード自体への依存はないので、全く別の環境にセットアップしても構いません。

以下の説明で、ディレクトリ構成などは、お使いの環境に合わせて適宜読み替えてください。

前提環境

下記の環境を必要とします。

  • Mocha (こちら)
  • JavaScriptの開発環境一式 (Node, IDE, npm, など)
  • Alexa Skills Kit (ASK) CLI (こちら)
  • TypeScript (オプション)

またテスト対象として、下記のスキルが作成されているものとします。

  • 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を使って取得します。詳細は下記を参照してください。

SMAPIで使用するアクセストークンの取得

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初版リリース
Alexa-Skill
スポンサーリンク