Building a test-driven chatbot for the Microsoft Bot Framework in Node.js

Stuart Harrison
Chatbots Life
Published in
6 min readApr 25, 2018

--

Chatbots are an old idea (I remember talking to chatbots on a creaky old 386 at school many moons ago), but thanks to the advances we’ve seen in artificial intelligence, and platforms such as Slack, Facebook and WhatsApp offering support for them, they really seem to be an idea who’s time has come.

I’ve recently been working on a chatbot project using the Microsoft Bot Framework, and I’ve been really happy with how it’s working. It took me a while to find a pleasing development flow, but now I’ve found it, I feel like it’s something I’d like to share.

Official support for the framework is offered for Node.js and .net. I’m no expert in either, but coupled with the experience I have in writing client side JavaScript and the fact that I’ve written a couple of Node.js packages, Node seemed like the obvious choice.

Assuming you have Node.js and Yarn installed, the first thing you’ll need to do is set up the project (shamelessly stolen from the official tutorial):

$ mkdir [YOUR_BOT_NAME]
$ cd [YOUR_BOT_NAME]
$ git init
$ npm init
$ yarn add dotenv
$ yarn add --dev nodemon
$ touch .env
$ echo ".env" >> .gitignore
$ echo "node_modules" >> .gitignore

Then register your bot:

  • Create a Microsoft Account (if necessary)
  • https://dev.botframework.com/bots/new
  • Create Microsoft App Name, ID and password (copy to clipboard)
  • Customize and Add the following to your .env file
MICROSOFT_BOT_APP_PASSWORD=[PASSWORD]
MICROSOFT_BOT_APP_ID=[YOUR_BOT_ID]
MICROSOFT_BOT_APP_NAME=[YOUR_BOT_NAME]

Top 3 Bot Tutorials

1. Which are the best chatbot frameworks?

2. How we wrote a Wirecutter-like bot with Reply.ai in just a few hours

3. How to Create a Facebook Messenger Bot with Ruby on Rails

Once you’ve done that, install the dependencies

$ yarn add botbuilder
$ yarn add --dev bot-tester
$ yarn add --dev mocha
$ yarn add --dev chai

Next create the dialog for your bot. Bots can be made of multiple dialogs — I like to separate out my dialogs, so they’re loosely coupled and testable.

$ mkdir lib
$ mkdir lib/dialogs
$ touch hello.js

Add the following code to the resulting hello.js file:

var builder = require('botbuilder');module.exports = [
function(session, results) {
builder.Prompts.text(session, 'What is your name?');
},
function(session, result) {
if (result.response == 'Stuart') {
session.endDialog('Hello friend')
} else {
session.endDialog('Welcome stranger')
}
}
]

This is a very simple dialog that asks the user’s name. If the name is Stuart it says Hello friend , otherwise, it says Welcome stranger.

We next need to create our actual bot and load that dialog into it. Create a file called bot.js in the main lib subdirectory:

$ touch lib/bot.js

And add the following code to the file:

var builder = require('botbuilder');
var hello = require('./dialogs/hello');
module.exports = function(connector) {
var bot = new builder.UniversalBot(connector).set('storage', inMemoryStorage);
var inMemoryStorage = new builder.MemoryBotStorage();
bot.dialog('/', hello);

return bot;
}

This builds our bot, and sets the hello dialog as the root dialog. Next, let’s set up our test environment!

The first thing to do is set up our test helper, which will set up everything we need to run our tests. Create a directory called test, and add a file called test_helper.js:

$ mkdir test
$ touch test_helper.js

Add the following code to test_helper.js:

var botBuilder = require('botbuilder');
var botTester = require('bot-tester');
var connector = new botTester.TestConnector({
defaultAddress: botBuilder.IAddress
});
var testBot = function(connector, dialogs) {
var bot = new botBuilder.UniversalBot(connector).set('storage', inMemoryStorage);
var inMemoryStorage = new botBuilder.MemoryBotStorage();

for (path in dialogs) {
bot.dialog(path, dialogs[path]);
}

return bot;
}
module.exports = {
connector: connector,
testBot: testBot,
botTester: botTester
}

You’ll notice here that, as well as requiring the bot-tester library, we’re also creating another bot! What gives?

Well, the bot we created will only be used later, once we set up our development and production environment. This bot has another argument, which allows us to set only the dialogs we want to test, meaning we don’t have to run through an entire conversation each time we run a test.

You’ll see what I mean in just a sec, when we create our dialog test. Create a folder called test/dialogs and a file called hello_test.js inside it:

$ mkdir test/dialogs
$ touch hello_test.js

Add this code to the resulting file:

var helper = require('../test_helper');
var hello = require('../../lib/dialogs/hello');
describe('hello', function() {
let bot;
let botTester;
this.timeout(15000);

beforeEach(function() {
bot = helper.testBot(helper.connector, {
'/': hello
})
botTester = new helper.botTester.BotTester(bot)
});

it('says hello to Stuart', function() {
return botTester
.sendMessageToBot('Get Started', 'What is your name?')
.sendMessageToBot('Stuart', 'Hello friend')
.runTest();
})

it('says hello to a stranger', function() {
return botTester
.sendMessageToBot('Get Started', 'What is your name?')
.sendMessageToBot('Martin', 'Welcome stranger')
.runTest();
})

})

You can see that we’re requiring our helper, and then in the beforeEach part of the test, we’re creating a new testable bot, which takes the test connector and an object with the path and location of the dialog we want to test.

Then, we can actually test the bot gives us the responses we expect. This is done by chaining sendMessageToBot methods, which take a user’s input and the bot’s expected response as arguments. We have two calls to that method in each test. The first call is the conversation opener, which can be anything and opens our dialog, which asks us our name. The second call is the user sending their name, and the expected response.

Next, we want to actually run the tests, so, set up your test command in the package.json file in the root of the project, replacing this line:

  "scripts": {
"test": "echo \"Error: no test specified\" && exit 1"
},

With this line:

  "scripts": {
"test": "./node_modules/mocha/bin/_mocha test/*"
},

Then you can run your tests!

$ yarn test

If everything’s gone OK, you should see something like the following:

yarn test v0.27.5$ ./node_modules/mocha/bin/_mocha test/*
hello✓ says hello to Stuart (564ms)✓ says hello to a stranger (513ms)2 passing (1s)Done in 2.30s.

Now, we’re happy with our tests, we can move on to running our bot in development mode. First, we need to install our webserver, which will listen for input and trigger our bot:

yarn add restify

Next, create a file calledindex.js in the root of the project:

touch index.js

And add the following code:

require('dotenv').config()var restify = require('restify');
var builder = require('botbuilder');
var server = restify.createServer();
server.listen(process.env.port || process.env.PORT || 3978, function () {
console.log('%s listening to %s', server.name, server.url);
});
var connector = new builder.ChatConnector({
appId: process.env.MICROSOFT_BOT_APP_ID,
appPassword: process.env.MICROSOFT_BOT_APP_PASSWORD
});
var bot = require('./lib/bot')(connector);server.post('/api/v1/messages', connector.listen());

Next, we need somewhere to actually test our bot out. The bot framework allows us to interface with Facebook, Slack and other services, but we can also test it out in development mode using the Bot Framework Emulator. Follow the instructions to install it on your system, and start it up.

Then add the URL of your local bot (which in this case will be http://localhost:3978/api/v1/messages), together with your bot’s app ID and password and click ‘Connect’:

You can then test your bot as if you were an end user chatting to it:

You also get a bunch of useful debug details on what information to being sent to and from your bot.

And that’s it! You can peep the documentation to dive into the framework more yourself, and also check out the BotTester documentation for more details on testing your bot.

--

--