Jestのモックを使ってインテント別にalexa-sdkで実装したコードをテストする

この記事は一人Alexa Skills Kit for Node.js Advent Calendar 2017の12日目の記事です。 alexa-sdkでコードを書く場合、以下のようなコードになります。 const A […]

広告ここから
広告ここまで

目次

    この記事は一人Alexa Skills Kit for Node.js Advent Calendar 2017の12日目の記事です。

    alexa-sdkでコードを書く場合、以下のようなコードになります。

    const Alexa = require('alexa-sdk')
    const handlers = {
      LaunchRequest: function () {
        alexa.emit(':tell', 'Hello World!')
      }
    }
    
    module.exports.handler = function (event, context, callback) {
      const alexa = Alexa.handler(event, context)
      alexa.registerHandlers(handlers)
      alexa.execute()
    }
    

    これまで紹介したテストは、基本的にAWS Lambdaがコールするmodule.exports.handlerでテストをしていました。

    今回はもうすこしスコープを小さく絞って以下の部分をテストしてみたいと思います。

    LaunchRequest: function () {
        alexa.emit(':tell', 'Hello World!')
      }
    

    テストできるように分割する

    ぱっと見たところ問題点は2つあります。

    • exportsされていないのでLaunchRequest()をテストファイルからよべない
    • this.emitのようにthisに依存している関数がある

    ということでまずはこの2つを解消しましょう。

    handlerを別ファイルに切り出す

    とりあえずファイルを分割しましょう。

    変更後のhandlers.js

    module.exports = {
      LaunchRequest: function () {
        alexa.emit(':tell', 'Hello World!')
      }
    }
    

    これで以下の例のようにLaunchRequest単体をよびだせるようになりました。

    const handlers = require('handlers')
    handlers.LaunchRequest()
    

    変更後のindex.js

    元ファイルの方も切り出したものをつかうように変更します。

    const Alexa = require('alexa-sdk')
    const handlers = require('handlers')
    
    module.exports.handler = function (event, context, callback) {
      const alexa = Alexa.handler(event, context)
      alexa.registerHandlers(handlers)
      alexa.execute()
    }
    

    ここでこれまで紹介したようなテストをまわしておくと、ちゃんと動くか確認できます。

    「接合部」を作ってテスト可能なコードにする

    このままではhandlers.LaunchRequest()を実行しても、this.emitがないというエラーが出るだけです。

    ということでt_wadaさんのセッションを参考に「接合部」を作るようにします。

    https://speakerdeck.com/twada/testable-lambda-working-effectively-with-legacy-lambda?slide=37
    参考:Testable Lambda: Working Effectively with Legacy Lambda P.37

    変更後のhandlers.js

    LaunchRequestの中で実行するのではなく、makeResponse関数にthisを渡してそちらで実行するようにしました。
    同時にmakeResponseについてもテストファイルからよめるようにexportしておきます。

    const handlers = {
      LaunchRequest: function () {
        makeResponse(this)
      }
    }
    
    function makeResponse (alexa) {
      alexa.emit(':tell', 'Hello World!')
    }
    
    exports.handlers = handlers
    exports.makeResponse = makeResponse
    

    変更後のindex.js

    exportの仕方が変わったため、こちらも変更しておきます。

    const Alexa = require('alexa-sdk')
    const libs = require('handlers')
    
    module.exports.handler = function (event, context, callback) {
      const alexa = Alexa.handler(event, context)
      alexa.registerHandlers(libs.handlers)
      alexa.execute()
    }
    

    これで準備ができました。

    Jestでテストを書く

    ここからはJestでテストを書いていきます。

    LaunchRequest.test.js

    ということで作成したものがこちらです。

    /* global describe, it, jest, expect */
    const handlers = require('./handlers')
    
    describe('LaunchRequest', () => {
      // emit()のモックを作成する
      const mockAlexa = {
        emit: jest.fn()
      }
      it('makeResponse()', () => {
        handlers.makeResponse(mockAlexa)
    
        // emit()が1度だけコールされていることをテストする
        expect(mockAlexa.emit.mock.calls.length).toBe(1)
    
        // emit()の引数が正しいものかをテストする
        expect(mockAlexa.emit.mock.calls[0][0]).toBe(':tell')
        expect(mockAlexa.emit.mock.calls[0][1]).toBe('Hello World!')
      })
    })
    
    

    テストを実行する

    あとはテストを実行するだけです。

    $ ./node_modules/jest/bin/jest.js 
     PASS  __tests__/LaunchRequest.test.js
    
    Test Suites: 1 passed, 1 total
    Tests:       1 passed, 1 total
    Snapshots:   0 total
    Time:        1.504s
    Ran all test suites.
    
    

    おわりに

    今回紹介した例では1行だけしかない関数のテストでしたが、外部のAPIと接続したり複雑な処理を走らせたい場合などに便利かなと思います。

    Lambdaのhandlerを通してのテストの場合、eventの値をフルで渡す必要があったりcontext.succeedcontext.failのそれぞれにアサーションを書かないとだったりなので、手早くテストしたい場合にもおすすめです。

    広告ここから
    広告ここまで
    Home
    Search
    Bookmark