Macadamian Blog

Unit Test Alexa Skills Locally

Christian Nadeau

Is it even possible to test an Alexa skill? That’s the question our lab team was asking. We outline a solution to test Alexa skills locally.

unit testing alexa skills locally

If you need to send an intent to the Amazon Echo service and expect a response from it, and you have no control over that service, is it even possible to test an Alexa skill?

That is what we were thinking, but what we didn’t realize at first glance is that the sample code in the AlexaSkill.js base class is totally stateless and is configured to send the intent. All you would have to do is define intent handlers and parse/process/return the desired result.

This means, if you control the Amazon Echo service response, you can perform some validations. Here is how we did it.
Let’s say you have a calculation skill that adds 2 numbers with IntentSchema.json:

{
 "intents": [{
     "intent": "CalcIntent",
     "slots": [{
       "name": "left",
       "type": "AMAZON.NUMBER"
     }, {
       "name": "right",
       "type": "AMAZON.NUMBER"
   }]
 }
}

and some utterances defined in SampleUtterances:

what is {left} plus {right}

It can have an intent handler that looks like:

function CalcIntent(intent, session, response) {
 // Note: no validation at all, very simple demo code
 let left = intent.slots.left.value;
 let right = intent.slots.right.value;
 let result = parseInt(left) + parseInt(right);
 response.tell(result);
};
This skill’s handler will receive an intent object similar to:
"intent": {
 "name": "CalcIntent",
 "slots": {
   "left": {
     "name": "left",
     "value": "2"
   },
   "right": {
     "name": "right",
     "value": "3"
   }
 }
}
You are expecting 5 as an answer in a result object similar to:
{
 "version": "1.0",
 "response": {
   "outputSpeech": {
     "type": "PlainText",
     "text": "5"
   },
   "shouldEndSession": true
 },
 "sessionAttributes": {
 }
}

This means that you are able to validate the result of your skill’s logic locally without even deploying:

const chai = require('chai');
const chaiAsPromised = require('chai-as-promised');
const expect = chai.expect;
const sinon = require('sinon');
const sinonChai = require('chai-sinon');
chai.use(chaiAsPromised);
chai.use(sinonChai);
describe('CalcIntent', () => {
 it('should return the sum', () => {
   // Get the intent handler function
   let handler = skillInstance.intentHandlers.CalcMeIntent;
   // Define the intent input
   let intent = {
     'name': 'sumIntent',
     'slots': {
       'left': {
         'name': 'left',
         'value': "2"
       },
       'right': {
         'name': 'right',
         'value': "3"
       }
     }
   };
   // Stub the response object using Sinon to be able to validate
   // what is the response sent by the intent handler
   let response = {
     tell: sinon.stub(),
     ask: sinon.stub()
   };
   // Empty session since this intent does not need it
   let session = {};
   // Call the intent
   handler(intent, session, response);
   // Assert the result was told
   expect(response.tell).to.have.been.calledWith(5);
 });
});

That’s it, you just validated your skills logic without having to deploy it on the cloud!

This post originally appeared on Medium

 

Insights delivered to your inbox

Subscribe to the dev blog to get the latest insights in IoT, Alexa Skills development, and software development.

Tags:

Author Overview

Christian Nadeau

Christian is a veteran software developer at Macadamian with specialties in .NET ( WPF, Silverlight, Window Phone 8 ), java (J2EE, JBoss), C++ (Qt, BB10). He holds a bachelor's degree in computer engineering from the University of Sherbrooke.