Amazon Echoを使って、Ghost Inspectorのテストを自動化する

この記事は Livesense - 自 Advent Calendar 2017 19日目の記事です。

皆様、こんにちは。
インフラストラクチャーグループの @takemotto です。
お酒の場だけは、本当の自分を出せている気がします。普段から明るくいきたいものです。

今回のテーマは、「自」ということで、**「自動化」**について書きたいと思います。
よろしくお付き合いください。

# 何を自動化したか？
アプリケーションデプロイ後のブラウザ確認は、どのようにやってますでしょうか？
各自がサービスにアクセスして動作確認をやってしまうと、```属人化```や```確認漏れ```が発生しそうです。
私が関わっている[DOOR賃貸](http://chintai.door.ac/)では、[Ghost Inspector](https://ghostinspector.com/)(以下、GI)というクラウド型のテストサービスを導入してみました。
GIを導入した理由は以下の通りです。

* 当初はrails5.1から使用できるSystemTestCaseを使用しようとしたが、rails5.0であったため断念。
* SasSなので、基本テストコード以外はメンテしなくて良い
* Chromeの拡張で画面操作で簡単にテストを登録できるため、ディレクターさんでもテスト登録できる
* APIでもGIのテスト叩けるため、自動化との相性が良さそう
* （頑張れば）Google Spreadsheetと連携できそう

以下のような構成にしました。

![AWS Design (1).png](https://qiita-image-store.s3.amazonaws.com/0/95324/8b8648cb-7a03-3a89-d8fd-2ad837f474cc.png)

これにより

* CodeDeployの「DeploymentSuccess」イベントをフックして、自動でテストを走らせる
* (別途実装が必要ですが)chatopsでテストを走らせる

といったことが可能となりました。

今回は、上記の仕組みに加えて、**Amazon Echo(Alexa)からGIのテストを実行**させてみたいと思います。　※赤枠部分を追加

![AWS Design.png](https://qiita-image-store.s3.amazonaws.com/0/95324/bd9c6fad-44fa-b0b6-6483-34290af00bc5.png)


# 参考にした情報
[Alexaスキル開発トレーニング](https://developer.amazon.com/ja/alexa-skills-kit/training/building-a-skill)を参考にしました。クラスメソッドさん、いつも大変お世話なっております。
Alexaで出てくる```インテント```や```スロット```といった用語が丁寧に解説されています。まずはこちらに目を通して頂けると理解しやくなると思います。非常に分かりやすい資料でした。

今回使用した設定は、

* [第1回 初めてのスキル開発](https://developer.amazon.com/ja/blogs/alexa/post/6e716e5c-55b0-445b-b936-9cfac4712e7b/training-1)
* [第2回 対話モデルとAlexa SDK](https://developer.amazon.com/ja/blogs/alexa/post/07377568-2815-4028-8c21-409dd8e84fa2/alexa-training-jp-2nd)

をやればカバーできると思います。

# 事前準備
事前に以下のアカウント作成します。

* [AWS](https://aws.amazon.com/jp/console/)のアカウント作成
* [Amazon Developer Services](https://developer.amazon.com/ja/)のアカウント作成
* [GI](https://ghostinspector.com/)のアカウント(freeプランで良い)作成


# 設定
大まかな処理の流れは、
**ユーザ ⇔ Amazon Echo ↔ Alexaサービス ⇔ Lambda ⇔ GI**
となっています。
GI、Lambda、Alexaの順で設定していきます。
それではさっそく設定してみましょう。

## Ghost Inspectorの設定
### Suiteの作成
GIにログイン後、Suite(テストをまとめる単位)を作成します。
今回は、本番環境とステージング環境を分けてテストしたいので、```production```と```staging```というSuiteを作成しました。

### テストの作成
作成したSuites毎にテストを登録します。
テストの登録は、Chromeの方であれば [こちらの拡張機能](https://chrome.google.com/webstore/detail/ghost-inspector-automated/aicdiabnghjnejfempeinmnphllefehc) を使用すれば、簡単にテスト登録が可能です。
拡張ツールを有効後、メニューバーにある![スクリーンショット 2017-12-13 16.57.38.png](https://qiita-image-store.s3.amazonaws.com/0/95324/c4be27fa-fd39-128e-268a-1353ccf03c1f.png)をクリックし、
「Start Recordig」ボタンをクリックすればテスト登録を開始できます。

例）テスト登録画面
![スクリーンショット 2017-12-13 16.56.38.png](https://qiita-image-store.s3.amazonaws.com/0/95324/85b31cec-dd65-3a5b-76b0-124637ea197b.png)

### Suite IDとAPI Keyを控える
APIの実行に必要な```Suite ID```と```API Key```を控えます。この後、Lambdaの環境変数として設定します。
```Suite ID```は、各Suiteページの画面右上```Settings```から確認できます。
```API Key```は、画面右上のアカウント名を選択すると表示される```Account Settings```から確認できます。

## AWS Lambdaの作成の設定
### 関数の作成
次に、Lambdaを作成していきます。以下の条件で関数を作成します。

|設定項目|値|補足|
|:--|:--|:--|
|設計図|alexa-skill-kit-sdk-factskill|nodejsのみです|
|名前*|任意|
|ロール|既存のロール|
|カスタムロールの作成|任意|AWSLambdaFullAccessを付与します。|

### 関数のコード
以下のコードを設定します。
注意点としては、Node.jsが非同期であるため、Callback関数を使用してAPIをたたく必要があるようです。

Stack Overflowの[こちらの投稿](https://stackoverflow.com/questions/42354742/amazon-aws-lambda-alexa-http-get-issue)を参考にしました。

```
"use strict";

var Alexa = require('alexa-sdk');
var https = require('https');
// var qs = require('qs');

// Lambda関数のメイン処理
exports.handler = function(event, context, callback) {
    var alexa = Alexa.handler(event, context); // Alexa SDKのインスタンス生成
    // alexa.appId = process.env.ALEXA_APPLICATION_ID;
    alexa.registerHandlers(handlers); // ハンドラの登録
    alexa.execute();                  // インスタンスの実行
};

var handlers = {
    // インテントに紐付かないリクエスト
    'LaunchRequest': function () {
    this.emit('AMAZON.HelpIntent'); // AMAZON.HelpIntentの呼び出し
    },
    // スキルの使い方を尋ねるインテント
    'AMAZON.HelpIntent': function () {
        this.emit(':tell', 'ゴーストインスペクターを実行します。' +
            'たとえば、ステージングをテストしてといってください。');
    },
    // 対話モデルで定義した、ゴーストインスペクターを実行するインテント
    'Ghostinspector': function () {
        var slot_env = this.event.request.intent.slots.Env.value; // スロットEnvを参照
        
        if(slot_env === 'ステージング'){
            console.log(slot_env);
            var suitesid = process.env.GI_SUITES_STG;
            var starturl = process.env.START_URL_STG;
        } else if (slot_env === 'プロダクション') {
            console.log(slot_env);
            var suitesid = process.env.GI_SUITES_PRD;
            var starturl = process.env.START_URL_PRD;
        } else {
            var error_message = slot_env + 'という環境はありません。';
            this.emit(':tell', error_message); // レスポンスの生成
        }
        var response = '';
        var self = this;
        var gi_api_key = process.env.GI_API_KEY;
        var paths = '/v1/suites/' + suitesid + '/execute/';
        var query = '?apiKey=' + gi_api_key + '&startUrl=' + starturl;
        var options = {
            protocol: 'https:',
            host: 'api.ghostinspector.com',
            path: paths + query,
            method: 'GET',
        };
        getHttps(options, function (response){
            var response = response;
            if (/SUCCESS/.test(response)) {
                self.emit(':tell', 'おめでとうございます。ゴーストインスペクターのテストは成功しました');
            } else {
                self.emit(':tell', 'ゴーストインスペクターのテストは失敗しました。詳細はヒップチャットを確認してください。');
            }
            
        }
    )},
};

function getHttps(options, callback){
    var text = '';
    https.get(options, function(res) {
        console.error("Got response: " + res.statusCode);
        res.on("data", function(chunk) {
        console.error("BODY: " + chunk);
        text = '' + chunk;
        return callback(text);
    });
    }).on('error', function(e) {
        text = 'error' + e.message;
        console.error("Got error: " + e.message);
    });
}
```

### 環境変数
さきほど控えたGIの```Suite ID```や```API key```をLambdaの環境変数に設定します

|キー|バリュー|
|:--|:--|
|START_URL_STG|ステージングのURL|
|START_URL_PRD|プロダクションのURL|
|GI_API_KEY|さきほど控えたGIのAPI Key|
|GI_SUITES_STG|さきほど控えたステージング用Suite ID|
|GI_SUITES_PRD|さきほど控えたプロダクション用Suite ID|

### その他Lambda設定

|設定項目|値|
|:--|:--|
|トリガー|Alexa Skills Kit|
|ランタイム|Node.js 6.10|
|タイムアウト|5分|

Lambda関数を作成後、**画面右上あるARNを控えてください。**
Alexaの設定で使用します。


## Amazon Developer Servicesの設定
次に[Amazon Developer Services](https://developer.amazon.com/ja/) の設定を行います。
ログイン後、画面右上の```Developers Console```→```Alexa```タブ→```Alexa Skills Kit```から任意のスキルを作成します。　指定値以外はデフォルト値です。

### スキル情報
|設定項目|値|
|:--|:--|
|スキル名|Ghost Inspector|
|呼び出し名|ゴーストインスペクター|
### 対話モデル
#### インテントスキーマ
インテントは、音声に対応する処理を定義します。
AMAZON.* というインテントは、デフォルトで用意されているビルトインインテントです。スキルが最低限応えるべきインテントや、定番のインテントがあります。
今回は、別途```Ghostinspector```というインテントを用意しました。

```
{
  "intents": [
    {
      "slots": [
        {
          "name": "Env",
          "type": "ListOfEnv"
        }
      ],
      "intent": "Ghostinspector"
    },
    {
      "intent": "AMAZON.HelpIntent"
    },
    {
      "intent": "AMAZON.StopIntent"
    },
    {
      "intent": "AMAZON.CancelIntent"
    }
  ]
}
```

#### カスタムスロットタイプ
スロットとは、変数のようなものという説明がありました。
今回は、```ステージング```と```プロダクション```というスロットを定義しました。

|設定項目|値|
|:--|:--|
|タイプを入力|ListOfEnv|
|タイプ|ステージング <br> プロダクション|


#### サンプル発話
サンプルの発話を登録します。
```Env```には、さきほど定義したスロット(ステージング or プロダクション)が入ります。

```
Ghostinspector {Env} を テストして
Ghostinspector {Env} を テスト
Ghostinspector {Env} で テスト
Ghostinspector {Env} で テストして
Ghostinspector {Env}
```
### 設定
作成したLambdaを呼び出すよう設定します。

|設定項目|値|
|:--|:--|
|サービスエンドポイントのタイプ|AWS Lambda の ARN (Amazonリソースネーム）|
|デフォルト|さきほど控えたLambdaのArn|

## テストツールを使って、Alexa経由でGIを実行してみよう
それでは、さっそくテストを実行してみます。
Amazon Developer Servicesで作成したスキルの``テスト``ページに移動します。
```発話を入力してください```のところで、```プロダクションをテスト```と入力し、```Ghost Inspectorを呼び出す```ボタンをクリックしましょう。
例）発話テスト画面
![スクリーンショット 2017-12-13 18.03.44.png](https://qiita-image-store.s3.amazonaws.com/0/95324/5d3db54b-6330-442e-b6fb-3f08b6ba6729.png)

すると処理が開始され、応答待ち(GIのテスト完了待ち)となります。
例）応答待ち画面
![スクリーンショット 2017-12-17 11.21.35.png](https://qiita-image-store.s3.amazonaws.com/0/95324/072ac1e4-9047-a71e-e5fc-e46f171788d1.png)

テストが成功すると以下のようなレスポンスが返ってきます。
例）発話テストの成功画面
![スクリーンショット 2017-12-17 11.22.24.png](https://qiita-image-store.s3.amazonaws.com/0/95324/1fb7e24a-8939-8f5f-0d70-63daa6fe51ee.png)


GIにログインし、```production```Suiteの ```Suite Run History``` からテストの実行結果が確認できると思います。
これで準備は整いました。


## スキルをベータ版として登録する
Echoからテスト実行するためには、Amazon Developer Servicesから作成したスキルをベータ版として登録する必要があります。
そのためには、作成したスキルの左側の6つのタブすべてが記入されていないと、スキルのベータテストが登録できません。現状だと、```公開情報```と```プライバシーとコンプライアンス```が残っているので登録していきます。

### 公開情報
|設定項目|値|補足|
|:--|:--|:--|
|カテゴリー|Productivity|
|サブカテゴリー|Organizers & Assistants|
|テストの手順|任意|
|国と地域を選択する:|Japna|日本だけに配信としました。|
|スキルの簡単な説明|任意|
|スキルの詳細な説明|任意|
|サンプルフレーズ|アレクサ、ゴーストインスペクターでプロダクションをテストして <br> アレクサ、ゴーストインスペクターでプロダクションをテスト|
|キーワード|ゴーストインスペクター|
|画像|任意|小、大アイコンを指定されたサイズで両方登録しないといけません|


### プライバシーとコンプライアンス
|設定項目|値|補足|
|:--|:--|:--|
|このスキルを使って何かを購入をしたり、実際にお金を支払うことができますか？|いいえ|
|このスキルはユーザーの個人情報を収集しますか？|いいえ|
|このスキルは13歳未満の子供を対象にしていますか？|いいえ|
|輸出コンプライアンス|✓する|
|このスキルは広告を含みますか？|いいえ|

すべての登録が終わると**Skills Beta Testingで登録が可能となります**
以下のように招待したいAmazonアカウントを入れてテスターを追加し、```テストを開始```ボタンをクリックするとメールが飛んできます。

例)Skills Beta Testing画面
![Skill Beta Testing.png](https://qiita-image-store.s3.amazonaws.com/0/95324/42343c62-2a65-b9cd-97a9-a48622edc060.png)

スマホの場合、送られた来たメールのリンクにアクセスすると、インストールしているAmazon Alexaアプリが起動します。
スキルを有効にするかどうか聞いてきるので有効にします。

例） スキルの有効化
<img width="375" alt="unnamed.jpg" src="https://qiita-image-store.s3.amazonaws.com/0/95324/c993ee2a-4603-c750-4d7c-3c1f73b0ec4c.jpeg">

ようやく準備整いました。

## Amazon Echoから実行してみよう
Amazon Echoに「アレクサ、ゴーストインスペクターでプロダクションをテストして」と話しかけてみてください。がんばってくれるでしょう。


# まとめ

* ある程度予想はしてましたが、chatopsで十分だと思います。

# 最後に
明日は、　@hotatesan さんです。お楽しみに！