{"id":2121,"date":"2017-01-20T02:00:00","date_gmt":"2017-01-20T02:00:00","guid":{"rendered":"https:\/\/www.microsoft.com\/reallifecode\/index.php\/2017\/01\/20\/unit-testing-for-bot-applications\/"},"modified":"2020-03-15T05:55:28","modified_gmt":"2020-03-15T12:55:28","slug":"unit-testing-for-bot-applications","status":"publish","type":"post","link":"https:\/\/devblogs.microsoft.com\/ise\/unit-testing-for-bot-applications\/","title":{"rendered":"Unit Testing for Bot Applications"},"content":{"rendered":"<p><em>Image: <a href=\"https:\/\/www.flickr.com\/photos\/isherwoodchris\/6917253693\">Mr Robot has some RAM<\/a> by <a href=\"https:\/\/www.flickr.com\/photos\/isherwoodchris\/\">Chris Isherwood<\/a>, used by <a href=\"https:\/\/creativecommons.org\/licenses\/by-sa\/2.0\">CC BY 2.0<\/a><\/em><\/p>\n<h2 id=\"first-things-first\">First Things First<\/h2>\n<p>Writing code using ad-hoc technologies like <a href=\"https:\/\/dev.botframework.com\/\">Microsoft Bot Framework<\/a> (MBF) is fun and exciting.\nBut before rushing to code bots that can make coffee and send spaceships to the moon, you need to think about unit testing your code.<\/p>\n<p>Recently, we worked with <a href=\"http:\/\/moed.ai\/\">Moed.ai<\/a> on a bot that schedules tasks and manages resources\u2019 time slots.\nIn order to ensure the quality of their bot <strong>Moed.ai<\/strong> wanted the ability to unit-test their bot logic.<\/p>\n<p>This code story outlines the way we tackled the challenge of adding unit test support in Microsoft Bot Framework.<\/p>\n<h3 id=\"what-should-we-test\">What Should We Test?<\/h3>\n<p>The first question we tried answering was \u201cWhat are we trying to test?\u201d<\/p>\n<p>Bots built using MBF <a href=\"https:\/\/docs.botframework.com\/en-us\/csharp\/builder\/sdkreference\/dialogs.html\">dialogs<\/a> and <a href=\"https:\/\/docs.botframework.com\/en-us\/node\/builder\/chat\/IntentDialog\/\">intents<\/a> usually consist of code intertwined with framework code.\nMoreover, major parts of the framework require external connectivity for saving session states or querying <a href=\"http:\/\/www.luis.ai\">LUIS<\/a> models.<\/p>\n<p>In most projects, when we think about unit testing, we think about segregated sections of logic that receive input and are expected to return specific output.\nIn case this logic has external dependencies, we need to search for ways to abstract those dependencies and provide stubs during testing.<\/p>\n<p>This means we can take one of the following approaches:<\/p>\n<ol>\n<li>Move the project\u2019s code to a separate module or directory and remove any dependencies on MBF<\/li>\n<li>Find a way to test the conversational logic of the bot without external dependencies<\/li>\n<\/ol>\n<h3 id=\"code-separation-difficulty\">Code Separation Difficulty<\/h3>\n<p>Writing code for bots is a little different than writing code for other web-based applications.<\/p>\n<p>Writing code for bot applications looks a lot like describing the way a conversational flow may evolve.\nFor example, just like in a customer service conversation, the bot application waits for a request that it can understand from the user to start a meaningful conversation. Then, during a series of questions and answers, it collects more data while trying to understand and provide the user with the best service.\nTaking maintainability and readability into account, it is important that such conversations represented by your code are understandable.<\/p>\n<p>Moreover, in bot scenarios, many actions and decisions require a high level of dependency on MBF.<\/p>\n<p>Here are some examples of code that are dependent on MBF:<\/p>\n<div class=\"language-js highlighter-rouge\">\n<pre class=\"highlight\"><code><span class=\"c1\">\/\/ Prompting the user for input<\/span>\r\n<span class=\"nx\">builder<\/span><span class=\"p\">.<\/span><span class=\"nx\">Prompts<\/span><span class=\"p\">.<\/span><span class=\"nx\">choice<\/span><span class=\"p\">(<\/span><span class=\"nx\">session<\/span><span class=\"p\">,<\/span> <span class=\"s1\">'What would you like to do?'<\/span><span class=\"p\">,<\/span> <span class=\"p\">[<\/span> <span class=\"s2\">\"Play Games\"<\/span><span class=\"p\">,<\/span> <span class=\"s2\">\"Do serious work\"<\/span><span class=\"p\">,<\/span> <span class=\"s2\">\"Other\"<\/span> <span class=\"p\">]);<\/span>\r\n\r\n<span class=\"c1\">\/\/ Saving data in session variable<\/span>\r\n<span class=\"nx\">session<\/span><span class=\"p\">.<\/span><span class=\"nx\">conversationData<\/span><span class=\"p\">.<\/span><span class=\"nx\">userChoice<\/span> <span class=\"o\">=<\/span> <span class=\"s2\">\"Play games while appearing to work seriously\"<\/span><span class=\"p\">;<\/span>\r\n\r\n<span class=\"c1\">\/\/ Beginning new dialogs or sending a \"typing\" status<\/span>\r\n<span class=\"nx\">session<\/span><span class=\"p\">.<\/span><span class=\"nx\">send<\/span><span class=\"p\">(<\/span><span class=\"s2\">\"Give a raise to smart worker\"<\/span><span class=\"p\">);<\/span>\r\n<span class=\"nx\">session<\/span><span class=\"p\">.<\/span><span class=\"nx\">sendTyping<\/span><span class=\"p\">();<\/span>\r\n\r\n<span class=\"c1\">\/\/ Ending a conversation<\/span>\r\n<span class=\"nx\">session<\/span><span class=\"p\">.<\/span><span class=\"nx\">endDialog<\/span><span class=\"p\">(<\/span><span class=\"s2\">\"Good Bye\"<\/span><span class=\"p\">);<\/span>\r\n<\/code><\/pre>\n<\/div>\n<p>A conversational flow consists of multiple REST calls and replies between the user and the web server. Trying to separate our logic from the conversational flow in many cases left us with almost no code to test. Moreover, that separation left the conversational flow logic untested.<\/p>\n<p>When we thought about unit testing in the world of bots, we realized that conversational flow logic is intertwined with the core logic of our application. This realization led us to develop a unique approach to testing the bot.<\/p>\n<h2 id=\"how-do-we-test-conversational-flow-logic\">How Do We Test Conversational Flow Logic?<\/h2>\n<p>For the purpose of this case study, we create a sample <strong>Alarm Clock Bot<\/strong>. A working copy of this bot application is available in <a href=\"https:\/\/github.com\/CatalystCode\/alarm-bot-unit-testing.git\">this GitHub repo<\/a>.<\/p>\n<p>Let\u2019s look at the following breakdown of a request to the bot application:<\/p>\n<ul>\n<li>Receives request from API<\/li>\n<li>Directs request to MBF<\/li>\n<li>Performs dialog step with bot input<\/li>\n<li>Communicates back to MBF<\/li>\n<\/ul>\n<p>The code we want to test is <em>wedged<\/em> in the middle of this flow, but is entirely orchestrated by MBF.<\/p>\n<p>Microsoft Bot Framework offers a class called <a href=\"https:\/\/docs.botframework.com\/en-us\/node\/builder\/chat\/UniversalBot\/#consoleconnector\">ConsoleConnector<\/a>. This class enables simulation of communications with the bot object without requiring an external connection:<\/p>\n<div class=\"language-js highlighter-rouge\">\n<pre class=\"highlight\"><code><span class=\"kd\">var<\/span> <span class=\"nx\">builder<\/span> <span class=\"o\">=<\/span> <span class=\"nx\">require<\/span><span class=\"p\">(<\/span><span class=\"s1\">'botbuilder'<\/span><span class=\"p\">);<\/span>\r\n\r\n<span class=\"kd\">var<\/span> <span class=\"nx\">connector<\/span> <span class=\"o\">=<\/span> <span class=\"k\">new<\/span> <span class=\"nx\">builder<\/span><span class=\"p\">.<\/span><span class=\"nx\">ConsoleConnector<\/span><span class=\"p\">();<\/span>\r\n\r\n<span class=\"c1\">\/\/ BotToTest is the bot class exposing a collection of intents.<\/span>\r\n<span class=\"c1\">\/\/ To understand how to build such a class, follow the code in the Alarm Clock Bot repository<\/span>\r\n<span class=\"kd\">var<\/span> <span class=\"nx\">bot<\/span> <span class=\"o\">=<\/span> <span class=\"k\">new<\/span> <span class=\"nx\">BotToTest<\/span><span class=\"p\">(<\/span><span class=\"nx\">connector<\/span><span class=\"p\">);<\/span>\r\n<span class=\"nx\">bot<\/span><span class=\"p\">.<\/span><span class=\"nx\">on<\/span><span class=\"p\">(<\/span><span class=\"s1\">'send'<\/span><span class=\"p\">,<\/span> <span class=\"kd\">function<\/span> <span class=\"p\">(<\/span><span class=\"nx\">message<\/span><span class=\"p\">)<\/span> <span class=\"p\">{<\/span>\r\n  <span class=\"cm\">\/* Check returned message *\/<\/span>\r\n<span class=\"p\">});<\/span>\r\n\r\n<span class=\"nx\">connector<\/span><span class=\"p\">.<\/span><span class=\"nx\">processMessage<\/span><span class=\"p\">(<\/span><span class=\"s1\">'Hello World'<\/span><span class=\"p\">);<\/span>\r\n<\/code><\/pre>\n<\/div>\n<p>Next, let\u2019s see how to test multiple steps in a dialog.<\/p>\n<p>For this purpose we use a <code class=\"highlighter-rouge\">step<\/code> indicator to tell us the index of the message:<\/p>\n<div class=\"language-js highlighter-rouge\">\n<pre class=\"highlight\"><code><span class=\"kd\">var<\/span> <span class=\"nx\">step<\/span> <span class=\"o\">=<\/span> <span class=\"mi\">1<\/span><span class=\"p\">;<\/span>\r\n<span class=\"nx\">bot<\/span><span class=\"p\">.<\/span><span class=\"nx\">on<\/span><span class=\"p\">(<\/span><span class=\"s1\">'send'<\/span><span class=\"p\">,<\/span> <span class=\"kd\">function<\/span> <span class=\"p\">(<\/span><span class=\"nx\">message<\/span><span class=\"p\">)<\/span> <span class=\"p\">{<\/span>\r\n  \r\n  <span class=\"k\">switch<\/span><span class=\"p\">(<\/span><span class=\"nx\">step<\/span><span class=\"o\">++<\/span><span class=\"p\">)<\/span> <span class=\"p\">{<\/span>\r\n    <span class=\"k\">case<\/span> <span class=\"mi\">1<\/span><span class=\"err\">:<\/span>\r\n      <span class=\"nx\">assert<\/span><span class=\"p\">(<\/span><span class=\"nx\">message<\/span><span class=\"p\">.<\/span><span class=\"nx\">text<\/span> <span class=\"o\">==<\/span> <span class=\"s1\">'What would you like to do?'<\/span><span class=\"p\">);<\/span>\r\n      <span class=\"nx\">connector<\/span><span class=\"p\">.<\/span><span class=\"nx\">processMessage<\/span><span class=\"p\">(<\/span><span class=\"s1\">'Play Games'<\/span><span class=\"p\">);<\/span>\r\n      <span class=\"k\">break<\/span><span class=\"p\">;<\/span>\r\n\r\n    <span class=\"k\">case<\/span> <span class=\"mi\">2<\/span><span class=\"err\">:<\/span>\r\n      <span class=\"nx\">assert<\/span><span class=\"p\">(<\/span><span class=\"nx\">message<\/span><span class=\"p\">.<\/span><span class=\"nx\">text<\/span> <span class=\"o\">==<\/span> <span class=\"s1\">'When?'<\/span><span class=\"p\">);<\/span>\r\n      <span class=\"nx\">connector<\/span><span class=\"p\">.<\/span><span class=\"nx\">processMessage<\/span><span class=\"p\">(<\/span><span class=\"s1\">'Always'<\/span><span class=\"p\">);<\/span>\r\n      <span class=\"k\">break<\/span><span class=\"p\">;<\/span>\r\n\r\n    <span class=\"k\">case<\/span> <span class=\"mi\">3<\/span><span class=\"err\">:<\/span>\r\n      <span class=\"nx\">assert<\/span><span class=\"p\">(<\/span><span class=\"nx\">message<\/span><span class=\"p\">.<\/span><span class=\"nx\">text<\/span> <span class=\"o\">==<\/span> <span class=\"s1\">'Why?'<\/span><span class=\"p\">);<\/span>\r\n      <span class=\"nx\">connector<\/span><span class=\"p\">.<\/span><span class=\"nx\">processMessage<\/span><span class=\"p\">(<\/span><span class=\"s1\">'I'm too cool for school'<\/span><span class=\"p\">);<\/span>\r\n      <span class=\"k\">break<\/span><span class=\"p\">;<\/span>\r\n\r\n    <span class=\"k\">case<\/span> <span class=\"mi\">4<\/span><span class=\"err\">:<\/span>\r\n      <span class=\"nx\">assert<\/span><span class=\"p\">(<\/span><span class=\"nx\">message<\/span><span class=\"p\">.<\/span><span class=\"nx\">text<\/span> <span class=\"o\">==<\/span> <span class=\"s1\">'No problem'<\/span><span class=\"p\">);<\/span>\r\n      <span class=\"k\">break<\/span><span class=\"p\">;<\/span>\r\n\r\n    <span class=\"nl\">default<\/span><span class=\"p\">:<\/span>\r\n      <span class=\"nx\">assert<\/span><span class=\"p\">(<\/span><span class=\"kc\">false<\/span><span class=\"p\">);<\/span> <span class=\"c1\">\/\/ The conversation should have ended<\/span>\r\n  <span class=\"p\">}<\/span>\r\n  \r\n<span class=\"p\">});<\/span>\r\n<span class=\"nx\">connector<\/span><span class=\"p\">.<\/span><span class=\"nx\">processMessage<\/span><span class=\"p\">(<\/span><span class=\"s1\">'Hello World'<\/span><span class=\"p\">);<\/span>\r\n<\/code><\/pre>\n<\/div>\n<p>To enable easy addition of more tests to the suite, we moved the conversational flow to an external module that exports JSON and consumes that via a generic tester:<\/p>\n<p><code class=\"highlighter-rouge\">\/test\/dialog-flows\/context-switching.js<\/code>:<\/p>\n<div class=\"language-js highlighter-rouge\">\n<pre class=\"highlight\"><code><span class=\"nx\">module<\/span><span class=\"p\">.<\/span><span class=\"nx\">exports<\/span> <span class=\"o\">=<\/span> <span class=\"p\">[<\/span>\r\n  <span class=\"p\">{<\/span>\r\n    <span class=\"na\">out<\/span><span class=\"p\">:<\/span> <span class=\"s2\">\"set alarm in 10 seconds\"<\/span>\r\n  <span class=\"p\">},<\/span>\r\n  <span class=\"p\">{<\/span>\r\n    <span class=\"na\">in<\/span><span class=\"p\">:<\/span> <span class=\"s2\">\"What would you like to call your alarm?\"<\/span><span class=\"p\">,<\/span>\r\n    <span class=\"na\">out<\/span><span class=\"p\">:<\/span> <span class=\"s2\">\"test\"<\/span>\r\n  <span class=\"p\">},<\/span>\r\n  <span class=\"p\">{<\/span>\r\n    <span class=\"na\">in<\/span><span class=\"p\">:<\/span> <span class=\"sr\">\/^<\/span><span class=\"se\">(<\/span><span class=\"sr\">Creating alarm named \"test\" for<\/span><span class=\"se\">)<\/span><span class=\"sr\">\/i<\/span><span class=\"p\">,<\/span>\r\n    <span class=\"na\">out<\/span><span class=\"p\">:<\/span> <span class=\"s2\">\"delete alarm named test\"<\/span>\r\n  <span class=\"p\">},<\/span>\r\n  <span class=\"p\">{<\/span>\r\n    <span class=\"na\">in<\/span><span class=\"p\">:<\/span> <span class=\"s2\">\"Deleted the 'test' alarm.\"<\/span> <span class=\"c1\">\/\/ the message sent by the bot after a few seconds<\/span>\r\n  <span class=\"p\">}<\/span>\r\n<span class=\"p\">];<\/span>\r\n<\/code><\/pre>\n<\/div>\n<p><code class=\"highlighter-rouge\">\/test\/common.js<\/code><\/p>\n<div class=\"language-js highlighter-rouge\">\n<pre class=\"highlight\"><code><span class=\"kd\">function<\/span> <span class=\"nx\">testBot<\/span><span class=\"p\">(<\/span><span class=\"nx\">bot<\/span><span class=\"p\">,<\/span> <span class=\"nx\">messages<\/span><span class=\"p\">,<\/span> <span class=\"nx\">done<\/span><span class=\"p\">)<\/span> <span class=\"p\">{<\/span>\r\n  <span class=\"kd\">var<\/span> <span class=\"nx\">step<\/span> <span class=\"o\">=<\/span> <span class=\"mi\">1<\/span><span class=\"p\">;<\/span>\r\n  <span class=\"kd\">var<\/span> <span class=\"nx\">connector<\/span> <span class=\"o\">=<\/span> <span class=\"nx\">bot<\/span><span class=\"p\">.<\/span><span class=\"nx\">connector<\/span><span class=\"p\">();<\/span>\r\n  <span class=\"nx\">bot<\/span><span class=\"p\">.<\/span><span class=\"nx\">on<\/span><span class=\"p\">(<\/span><span class=\"s1\">'send'<\/span><span class=\"p\">,<\/span> <span class=\"kd\">function<\/span> <span class=\"p\">(<\/span><span class=\"nx\">message<\/span><span class=\"p\">)<\/span> <span class=\"p\">{<\/span>\r\n      \r\n    <span class=\"kd\">var<\/span> <span class=\"nx\">check<\/span> <span class=\"o\">=<\/span> <span class=\"nx\">messages<\/span><span class=\"p\">[<\/span><span class=\"nx\">step<\/span> <span class=\"o\">-<\/span> <span class=\"mi\">1<\/span><span class=\"p\">];<\/span>\r\n    \r\n    <span class=\"c1\">\/\/ Check input message<\/span>\r\n    <span class=\"k\">if<\/span> <span class=\"p\">(<\/span><span class=\"nx\">check<\/span><span class=\"p\">.<\/span><span class=\"k\">in<\/span><span class=\"p\">)<\/span> <span class=\"p\">{<\/span>\r\n      <span class=\"nx\">assert<\/span><span class=\"p\">(<\/span><span class=\"nx\">message<\/span><span class=\"p\">.<\/span><span class=\"nx\">text<\/span> <span class=\"o\">===<\/span> <span class=\"nx\">check<\/span><span class=\"p\">.<\/span><span class=\"k\">in<\/span><span class=\"p\">);<\/span>\r\n    <span class=\"p\">}<\/span>\r\n\r\n    <span class=\"c1\">\/\/ Send an output reply<\/span>\r\n    <span class=\"k\">if<\/span> <span class=\"p\">(<\/span><span class=\"nx\">check<\/span><span class=\"p\">.<\/span><span class=\"nx\">out<\/span><span class=\"p\">)<\/span> <span class=\"p\">{<\/span>\r\n      <span class=\"nx\">connector<\/span><span class=\"p\">.<\/span><span class=\"nx\">processMessage<\/span><span class=\"p\">(<\/span><span class=\"nx\">check<\/span><span class=\"p\">.<\/span><span class=\"nx\">out<\/span><span class=\"p\">);<\/span>\r\n    <span class=\"p\">}<\/span>\r\n\r\n    <span class=\"c1\">\/\/ End conversation in the last message<\/span>\r\n    <span class=\"nx\">step<\/span><span class=\"o\">++<\/span><span class=\"p\">;<\/span>\r\n    <span class=\"k\">if<\/span> <span class=\"p\">(<\/span><span class=\"nx\">step<\/span> <span class=\"o\">-<\/span> <span class=\"mi\">1<\/span> <span class=\"o\">==<\/span> <span class=\"nx\">messages<\/span><span class=\"p\">.<\/span><span class=\"nx\">length<\/span><span class=\"p\">)<\/span> <span class=\"p\">{<\/span>\r\n      <span class=\"nx\">done<\/span><span class=\"p\">();<\/span>\r\n    <span class=\"p\">}<\/span>\r\n  <span class=\"p\">});<\/span>\r\n<span class=\"p\">}<\/span>\r\n\r\n<span class=\"nx\">module<\/span><span class=\"p\">.<\/span><span class=\"nx\">exports<\/span> <span class=\"o\">=<\/span> <span class=\"p\">{<\/span>\r\n  <span class=\"nx\">testBot<\/span>\r\n<span class=\"p\">}<\/span>\r\n<\/code><\/pre>\n<\/div>\n<h3 id=\"testing-luis\">Testing LUIS<\/h3>\n<p>LUIS is an external service integrated into the MBF SDK and used by many bot applications for <em>intent recognition<\/em> and <em>entity extraction<\/em>. To enable testing of that service we need to mimic URL calls to LUIS. We used <a href=\"https:\/\/github.com\/node-nock\/nock\">Nock<\/a> to simulate LUIS\u2019s call, but <code class=\"highlighter-rouge\">Nock<\/code> can be used to mimic any URL calls.<\/p>\n<p>For the following mocked-up call, we made a GET call to LUIS with the appropriate query and copied the responses:<\/p>\n<div class=\"language-js highlighter-rouge\">\n<pre class=\"highlight\"><code>  <span class=\"nx\">nock<\/span><span class=\"p\">(<\/span><span class=\"s1\">'https:\/\/luis.url'<\/span><span class=\"p\">)<\/span>\r\n    <span class=\"p\">.<\/span><span class=\"nx\">get<\/span><span class=\"p\">(<\/span><span class=\"s1\">'\/?id=appId&amp;subscription-key=subId&amp;q='<\/span> <span class=\"o\">+<\/span> <span class=\"nb\">encodeURIComponent<\/span><span class=\"p\">(<\/span><span class=\"s1\">'set alarm test in 10 seconds'<\/span><span class=\"p\">))<\/span>\r\n    <span class=\"p\">.<\/span><span class=\"nx\">reply<\/span><span class=\"p\">(<\/span><span class=\"mi\">200<\/span><span class=\"p\">,<\/span> <span class=\"p\">{<\/span>\r\n      <span class=\"s2\">\"query\"<\/span><span class=\"p\">:<\/span> <span class=\"s2\">\"set alarm test in 10 seconds\"<\/span><span class=\"p\">,<\/span>\r\n      <span class=\"s2\">\"intents\"<\/span><span class=\"p\">:<\/span> <span class=\"p\">[<\/span>\r\n        <span class=\"p\">{<\/span>\r\n          <span class=\"s2\">\"intent\"<\/span><span class=\"p\">:<\/span> <span class=\"s2\">\"builtin.intent.alarm.set_alarm\"<\/span>\r\n        <span class=\"p\">}<\/span>\r\n        <span class=\"cm\">\/* ... *\/<\/span>\r\n      <span class=\"p\">],<\/span>\r\n      <span class=\"s2\">\"entities\"<\/span><span class=\"p\">:<\/span> <span class=\"p\">[<\/span>\r\n        <span class=\"p\">{<\/span>\r\n          <span class=\"s2\">\"entity\"<\/span><span class=\"p\">:<\/span> <span class=\"s2\">\"in 10 seconds\"<\/span><span class=\"p\">,<\/span>\r\n          <span class=\"s2\">\"type\"<\/span><span class=\"p\">:<\/span> <span class=\"s2\">\"builtin.alarm.start_time\"<\/span><span class=\"p\">,<\/span>\r\n          <span class=\"s2\">\"resolution\"<\/span><span class=\"p\">:<\/span> <span class=\"p\">{<\/span>\r\n            <span class=\"s2\">\"resolution_type\"<\/span><span class=\"p\">:<\/span> <span class=\"s2\">\"builtin.datetime.time\"<\/span><span class=\"p\">,<\/span>\r\n            <span class=\"s2\">\"time\"<\/span><span class=\"p\">:<\/span> <span class=\"s2\">\"2016-12-14T15:31:59\"<\/span> <span class=\"c1\">\/\/ some time in the past<\/span>\r\n          <span class=\"p\">}<\/span>\r\n        <span class=\"p\">}<\/span>\r\n      <span class=\"p\">]<\/span>\r\n    <span class=\"p\">})<\/span>\r\n    <span class=\"p\">.<\/span><span class=\"nx\">get<\/span><span class=\"p\">(<\/span><span class=\"s1\">'\/?id=appId&amp;subscription-key=subId&amp;q='<\/span> <span class=\"o\">+<\/span> <span class=\"nb\">encodeURIComponent<\/span><span class=\"p\">(<\/span><span class=\"s1\">'delete alarm named test'<\/span><span class=\"p\">))<\/span>\r\n    <span class=\"p\">.<\/span><span class=\"nx\">reply<\/span><span class=\"p\">(<\/span><span class=\"mi\">200<\/span><span class=\"p\">,<\/span> <span class=\"p\">{<\/span>\r\n      <span class=\"s2\">\"query\"<\/span><span class=\"p\">:<\/span> <span class=\"s2\">\"delete alarm named test\"<\/span><span class=\"p\">,<\/span>\r\n      <span class=\"s2\">\"intents\"<\/span><span class=\"p\">:<\/span> <span class=\"p\">[<\/span>\r\n        <span class=\"p\">{<\/span>\r\n          <span class=\"s2\">\"intent\"<\/span><span class=\"p\">:<\/span> <span class=\"s2\">\"builtin.intent.alarm.delete_alarm\"<\/span>\r\n        <span class=\"p\">}<\/span>\r\n      <span class=\"p\">],<\/span>\r\n      <span class=\"s2\">\"entities\"<\/span><span class=\"p\">:<\/span> <span class=\"p\">[<\/span>\r\n        <span class=\"p\">{<\/span>\r\n          <span class=\"s2\">\"entity\"<\/span><span class=\"p\">:<\/span> <span class=\"s2\">\"test\"<\/span><span class=\"p\">,<\/span>\r\n          <span class=\"s2\">\"type\"<\/span><span class=\"p\">:<\/span> <span class=\"s2\">\"builtin.alarm.title\"<\/span>\r\n        <span class=\"p\">}<\/span>\r\n      <span class=\"p\">]<\/span>\r\n    <span class=\"p\">});<\/span>\r\n<\/code><\/pre>\n<\/div>\n<h3 id=\"creating-the-suite\">Creating The Suite<\/h3>\n<p>Finally, to wrap it all up in a test suite, we used a <code class=\"highlighter-rouge\">describe<\/code> call:<\/p>\n<div class=\"language-js highlighter-rouge\">\n<pre class=\"highlight\"><code><span class=\"cm\">\/* requires *\/<\/span>\r\n\r\n<span class=\"kd\">var<\/span> <span class=\"nx\">historyMessages<\/span> <span class=\"o\">=<\/span> <span class=\"nx\">require<\/span><span class=\"p\">(<\/span><span class=\"s1\">'.\/dialog-flows\/history-intents'<\/span><span class=\"p\">);<\/span>\r\n<span class=\"kd\">var<\/span> <span class=\"nx\">switchingMessages<\/span> <span class=\"o\">=<\/span> <span class=\"nx\">require<\/span><span class=\"p\">(<\/span><span class=\"s1\">'.\/dialog-flows\/context-switch'<\/span><span class=\"p\">);<\/span>\r\n<span class=\"nx\">common<\/span><span class=\"p\">.<\/span><span class=\"nx\">setup<\/span><span class=\"p\">();<\/span>\r\n\r\n<span class=\"c1\">\/\/Our parent block<\/span>\r\n<span class=\"nx\">describe<\/span><span class=\"p\">(<\/span><span class=\"s1\">'Bot Tests'<\/span><span class=\"p\">,<\/span> <span class=\"p\">()<\/span> <span class=\"o\">=&gt;<\/span> <span class=\"p\">{<\/span>\r\n\r\n  <span class=\"nx\">it<\/span><span class=\"p\">(<\/span><span class=\"s1\">'should recognize history intents'<\/span><span class=\"p\">,<\/span> <span class=\"kd\">function<\/span> <span class=\"p\">(<\/span><span class=\"nx\">done<\/span><span class=\"p\">)<\/span> <span class=\"p\">{<\/span> \r\n      <span class=\"kd\">var<\/span> <span class=\"nx\">connector<\/span> <span class=\"o\">=<\/span> <span class=\"k\">new<\/span> <span class=\"nx\">builder<\/span><span class=\"p\">.<\/span><span class=\"nx\">ConsoleConnector<\/span><span class=\"p\">();<\/span>\r\n      <span class=\"kd\">var<\/span> <span class=\"nx\">bot<\/span> <span class=\"o\">=<\/span> <span class=\"nx\">TestedBot<\/span><span class=\"p\">.<\/span><span class=\"nx\">create<\/span><span class=\"p\">(<\/span><span class=\"nx\">connector<\/span><span class=\"p\">);<\/span>\r\n\r\n      <span class=\"nx\">common<\/span><span class=\"p\">.<\/span><span class=\"nx\">testBot<\/span><span class=\"p\">(<\/span><span class=\"nx\">bot<\/span><span class=\"p\">,<\/span> <span class=\"nx\">historyMessages<\/span><span class=\"p\">,<\/span> <span class=\"nx\">done<\/span><span class=\"p\">);<\/span>\r\n      \r\n      <span class=\"nx\">connector<\/span><span class=\"p\">.<\/span><span class=\"nx\">processMessage<\/span><span class=\"p\">(<\/span><span class=\"s1\">'hi'<\/span><span class=\"p\">);<\/span>\r\n  <span class=\"p\">});<\/span>\r\n\r\n  <span class=\"nx\">it<\/span><span class=\"p\">(<\/span><span class=\"s1\">'context switching'<\/span><span class=\"p\">,<\/span> <span class=\"kd\">function<\/span> <span class=\"p\">(<\/span><span class=\"nx\">done<\/span><span class=\"p\">)<\/span> <span class=\"p\">{<\/span> \r\n      <span class=\"kd\">var<\/span> <span class=\"nx\">connector<\/span> <span class=\"o\">=<\/span> <span class=\"k\">new<\/span> <span class=\"nx\">builder<\/span><span class=\"p\">.<\/span><span class=\"nx\">ConsoleConnector<\/span><span class=\"p\">();<\/span>\r\n\r\n      <span class=\"kd\">var<\/span> <span class=\"nx\">bot<\/span> <span class=\"o\">=<\/span> <span class=\"nx\">TestedBot<\/span><span class=\"p\">.<\/span><span class=\"nx\">create<\/span><span class=\"p\">(<\/span><span class=\"nx\">connector<\/span><span class=\"p\">);<\/span>       \r\n      <span class=\"nx\">common<\/span><span class=\"p\">.<\/span><span class=\"nx\">testBot<\/span><span class=\"p\">(<\/span><span class=\"nx\">bot<\/span><span class=\"p\">,<\/span> <span class=\"nx\">switchingMessages<\/span><span class=\"p\">,<\/span> <span class=\"nx\">done<\/span><span class=\"p\">);<\/span>\r\n      \r\n      <span class=\"nx\">connector<\/span><span class=\"p\">.<\/span><span class=\"nx\">processMessage<\/span><span class=\"p\">(<\/span><span class=\"s1\">'hi'<\/span><span class=\"p\">);<\/span>\r\n  <span class=\"p\">});<\/span>\r\n<span class=\"p\">});<\/span>\r\n\r\n<\/code><\/pre>\n<\/div>\n<p>To be able to do that, we needed the bot to expose a <code class=\"highlighter-rouge\">create<\/code> call that receives a <code class=\"highlighter-rouge\">connector<\/code> object:<\/p>\n<div class=\"language-js highlighter-rouge\">\n<pre class=\"highlight\"><code><span class=\"kd\">var<\/span> <span class=\"nx\">builder<\/span> <span class=\"o\">=<\/span> <span class=\"nx\">require<\/span><span class=\"p\">(<\/span><span class=\"s1\">'botbuilder'<\/span><span class=\"p\">);<\/span>\r\n\r\n<span class=\"kd\">function<\/span> <span class=\"nx\">create<\/span><span class=\"p\">(<\/span><span class=\"nx\">connector<\/span><span class=\"p\">)<\/span> <span class=\"p\">{<\/span>\r\n\r\n  <span class=\"kd\">var<\/span> <span class=\"nx\">bot<\/span> <span class=\"o\">=<\/span> <span class=\"k\">new<\/span> <span class=\"nx\">builder<\/span><span class=\"p\">.<\/span><span class=\"nx\">UniversalBot<\/span><span class=\"p\">(<\/span><span class=\"nx\">connector<\/span><span class=\"p\">);<\/span>\r\n  <span class=\"kd\">var<\/span> <span class=\"nx\">intents<\/span> <span class=\"o\">=<\/span> <span class=\"k\">new<\/span> <span class=\"nx\">builder<\/span><span class=\"p\">.<\/span><span class=\"nx\">IntentDialog<\/span><span class=\"p\">();<\/span>\r\n\r\n  <span class=\"nx\">bot<\/span><span class=\"p\">.<\/span><span class=\"nx\">dialog<\/span><span class=\"p\">(<\/span><span class=\"s1\">'\/'<\/span><span class=\"p\">,<\/span> <span class=\"nx\">intents<\/span><span class=\"p\">);<\/span>\r\n  <span class=\"cm\">\/* ... *\/<\/span>\r\n\r\n  <span class=\"k\">return<\/span> <span class=\"nx\">bot<\/span><span class=\"p\">;<\/span>\r\n<span class=\"p\">}<\/span>\r\n\r\n<span class=\"nx\">module<\/span><span class=\"p\">.<\/span><span class=\"nx\">exports<\/span> <span class=\"o\">=<\/span> <span class=\"p\">{<\/span> <span class=\"nx\">create<\/span> <span class=\"p\">};<\/span>\r\n<\/code><\/pre>\n<\/div>\n<p>This way, we can supply a <code class=\"highlighter-rouge\">builder.ChatConnector<\/code> in runtime and a <code class=\"highlighter-rouge\">builder.ConsoleConnector<\/code> in testing.<\/p>\n<h2 id=\"opportunities-for-reuse\">Opportunities for Reuse<\/h2>\n<p>You should reuse this code in any Microsoft Bot Framework project that requires unit testing for conversational flow.<\/p>\n<h2 id=\"repositories\">Repositories<\/h2>\n<p>This is a sample of building a <em>Set Alarm<\/em> project with integrated <strong>unit testing<\/strong>:\n<a href=\"https:\/\/github.com\/CatalystCode\/alarm-bot-unit-testing\">https:\/\/github.com\/CatalystCode\/alarm-bot-unit-testing<\/a>\nIt can also be used as a quickstart template for developing bots.<\/p>\n<p>To see the full solution integrated into a bot see:\n<a href=\"https:\/\/github.com\/CatalystCode\/multilingual-uber-bot\">https:\/\/github.com\/CatalystCode\/multilingual-uber-bot<\/a><\/p>\n","protected":false},"excerpt":{"rendered":"<p>Adding unit testing for conversational flow to Microsoft Bot Framework projects, including ones using LUIS.<\/p>\n","protected":false},"author":21371,"featured_media":11040,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"_acf_changed":false,"footnotes":""},"categories":[13],"tags":[110,249,261,367],"class_list":["post-2121","post","type-post","status-publish","format-standard","has-post-thumbnail","hentry","category-bots","tag-bots","tag-microsoft-bot-framework-mbf","tag-moed-ai","tag-unit-testing"],"acf":[],"blog_post_summary":"<p>Adding unit testing for conversational flow to Microsoft Bot Framework projects, including ones using LUIS.<\/p>\n","_links":{"self":[{"href":"https:\/\/devblogs.microsoft.com\/ise\/wp-json\/wp\/v2\/posts\/2121","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/devblogs.microsoft.com\/ise\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/devblogs.microsoft.com\/ise\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/ise\/wp-json\/wp\/v2\/users\/21371"}],"replies":[{"embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/ise\/wp-json\/wp\/v2\/comments?post=2121"}],"version-history":[{"count":0,"href":"https:\/\/devblogs.microsoft.com\/ise\/wp-json\/wp\/v2\/posts\/2121\/revisions"}],"wp:featuredmedia":[{"embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/ise\/wp-json\/wp\/v2\/media\/11040"}],"wp:attachment":[{"href":"https:\/\/devblogs.microsoft.com\/ise\/wp-json\/wp\/v2\/media?parent=2121"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/ise\/wp-json\/wp\/v2\/categories?post=2121"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/ise\/wp-json\/wp\/v2\/tags?post=2121"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}