Package: lex-bot-tester
bannerlex-bot-testerAWS Lex Bot Testeris a library that simplifies the creation of
Amazon Alexa Skills and AWS Lex Bot functional tests.While this framework has been developed in python (Node version in in
the roadmap) it can be used to test Skills and Bots developed in any
language.Using AWS Lex Models Client this utility inspects the properties of the
available Bots and creates specific Results classes to be used by the
tests.Certainly, there are ways of testing your bots using AWS CLI as
explained inTest the Bot Using Text Input (AWS
CLI)butlex-bot-testerprovides a more concise, type safe and object
oriented way of doing it.InstallationRunpip install lex-bot-testerExamplesAlexa Skill test exampleThis example uses Alexa Skill Blueprint: BookMyTrip.Usinglex-bot-testerconversational tests like the following one can
be created with minimal effort.classAlexaSkillManagementClientTests(AlexaSkillTest):deftest_book_my_trip_reserve_a_car(self):skill_name='BookMyTripSkill'intent='BookCar'conversation=[{'slot':None,'text':'ask book my trip to reserve a car'},{'slot':'CarType','text':'midsize'},{'slot':'PickUpCity','text':'buenos aires'},{'slot':'PickUpDate','text':'tomorrow'},{'slot':'ReturnDate','text':'five days from now'},{'slot':'DriverAge','text':'twenty five'},{'slot':None,'prompt':'Confirmation','text':'yes'}]self.conversation_text(skill_name,intent,conversation,verbose=True)you can see full source code atalexaskillmanagementclienttests.py.Running the testsYou can run the tests from your favorite IDE or from the command line.If you are interested in seeing the details of the conversation you can
add the--verboseoption to the test runner.$ ./alexaskillmamagementclienttests.py --verboseand you will see an interaction similar to this onetest runAWS Lex Bot test exampleYou may be familiar with this kind of tests in theAWS Lex Console(this example uses the well knowOrderFlowersbot).test-botMore information about these manual tests using the console can be
foundhereHowever, once you have thelex-bot-testerinstalled, you can create
tests like this one:#! /usr/bin/env python# -*- coding: utf-8 -*-"""
    Lex Bot Tester
    Copyright (C) 2017  Diego Torres Milano

    This program is free software: you can redistribute it and/or modify
    it under the terms of the GNU General Public License as published by
    the Free Software Foundation, either version 3 of the License, or
    (at your option) any later version.

    This program is distributed in the hope that it will be useful,
    but WITHOUT ANY WARRANTY; without even the implied warranty of
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    GNU General Public License for more details.

    You should have received a copy of the GNU General Public License
    along with this program.  If not, see <http://www.gnu.org/licenses/>.
"""importreimportunittestfromlex_bot_tester.aws.lex.conversationimportConversation,ConversationItemfromlex_bot_tester.aws.lex.lexbottestimportLexBotTestfromlex_bot_tester.aws.lex.lexmodelsclientimportLexModelsClientfromlex_bot_tester.aws.lex.lexruntimeclientimportDialogStateRE_DATE=re.compile('\d+-\d+-\d+')BOT_NAME='OrderFlowers'BOT_ALIAS='OrderFlowersLatest'USER_ID='ClientId'classOrderFlowersTests(LexBotTest):deftest_conversations_text(self):lmc=LexModelsClient(BOT_NAME,BOT_ALIAS)conversations=[]foriinlmc.get_intents_for_bot():r=lmc.get_result_class_for_intent(i)ifi=='OrderFlowers':conversations.append(Conversation(ConversationItem('I would like to order some roses',r(DialogState.ELICIT_SLOT,flower_type='roses')),ConversationItem('white',r(DialogState.ELICIT_SLOT,flower_type='roses',flower_color='white')),ConversationItem('next Sunday',r(DialogState.ELICIT_SLOT,flower_type='roses',flower_color='white',pickup_date=RE_DATE)),ConversationItem('noon',r(DialogState.CONFIRM_INTENT,flower_type='roses',flower_color='white',pickup_date=RE_DATE,pickup_time='12:00')),ConversationItem('yes',r(DialogState.FULFILLED,flower_type='roses',flower_color='white',pickup_date=RE_DATE,pickup_time='12:00')),))elifi=='Cancel':conversations.append(Conversation(ConversationItem('Cancel',r(DialogState.READY_FOR_FULFILLMENT))))self.conversations_text(BOT_NAME,BOT_ALIAS,USER_ID,conversations)if__name__=='__main__':unittest.main()This test, first creates aLexModelsClientto inspect the
definitions of the bot, its intents and slots to later use a class
factory that defines specific classes for each intent which are obtained
byget_result_class_for_intent(i).This result class reference, which extendsResultBaseclass is
assigned to the variablerfor convenience. Then, for each intent, aConversation, consisting of a list ofConversationItemsis
created.ConversationItemspecifies the text or utterance sent and the
expected result, using therclass reference and invoking the
constructor with the expectedDialogStateand the values of theslots.pickup_dateis a particular case, as it is selected asnext Sundayso instead of looking for a particular value we are
checking if it matches a regular expression defining dates.Finally, once theconversationlist is completed, a call to the
helper methodconversations_textproviding this list as an argument
completes the test.However, if you are more into a data-driven approach, you can also
declare the conversation as a data structure as shown in the following
example.deftest_conversations_text_book_car(self):bot_name='BookTrip'bot_alias='BookTripLatest'user_id='ClientId'conversation_definition={'BookCar':[('book a car',DialogState.ELICIT_SLOT,{}),('L.A.',DialogState.ELICIT_SLOT,{}),('next week',DialogState.ELICIT_SLOT,{'PickUpDate':RE_WEEK}),('a month from now',DialogState.ELICIT_SLOT,{'ReturnDate':RE_DATE}),('25',DialogState.ELICIT_SLOT,{}),('economy',DialogState.CONFIRM_INTENT,{}),('yes',DialogState.READY_FOR_FULFILLMENT,{}),],'Cancel':[('cancel',DialogState.READY_FOR_FULFILLMENT,{})]}self.conversations_text_helper(bot_alias,bot_name,user_id,conversation_definition,verbose)Both approaches are identical in functionality, so you can choose the
one that suits your taste.Result classesAs mentioned before,LexModelsClient.get_result_class_for_intent(intent)returns the
class that represents the response result once the Bot is invoked using
the corresponding utterance.The signature of the constructor matches this patternclass MyIntentResult(ResultBase):
    def __init__(dialog_state, **kwargs):
        ...To comply withPEP
8,
keyword args representing slots are named usingsnake casewhen
usually slots are named usingcamel case. Then, for example, the slotFlowerTypewill be represented by its corresponding keyword argflower_type.ConversationsConversationis a list ofConversationItems. TheseConversationItemsrepresent thesend->responseinteraction.class ConversationItem(object):

    def __init__(self, send, receive):
        ...Perhaps, taking a look atlexbottestertests.pyclarifies the idea. That test, uses the same structure and the classes
created by inspecting the models for two different Bots: OrderFlowers
and BookTrip.Running the testsYou can run the tests from your favorite IDE or from the command line.If you are interested in seeing the details of the conversation you can
add the--verboseoption to the test runner.$ ./lexbottesttests.py --verboseand you will see an interaction similar to the one presented before.term-outputResourcesTesting Alexa Skills - The grail
questCreating conversational AWS Lex Bot
testsImproving conversational AWS Lex Bot
tests