-
Notifications
You must be signed in to change notification settings - Fork 20
Writing tests
It is possible to use a test-driven-development style workflow with vumi-go javascript sandbox applications.
Here's a basic two test skeleton break-down:
First include the prereqs and the vumigo application itself:
var fs = require("fs");
var assert = require("assert");
var vumigo = require("vumigo_v01");
Then link to your application which should be in lib
directory (leave off the
.js extension)
var app = require("../lib/vumi-go-skeleton");
Next, run a set of tests to ensure you've correctly hooked the InteractionMachine up to the api correctly and called im.attach();
describe("test_api", function() {
it("should exist", function() {
assert.ok(app.api);
});
it("should have an on_inbound_message method", function() {
assert.ok(app.api.on_inbound_message);
});
it("should have an on_inbound_event method", function() {
assert.ok(app.api.on_inbound_event);
});
});
Now begin your application specific tests. If you use the -R spec
flag when calling
mocha to run these tests, the tests under each describe("Some group of tests")
will be shown above the tests.
describe("When using the skeleton", function() {
Fixtures are there to mock API responses (such as a the google maps API). This allows
you to test regardless of connection to the Internet and ensures you have a consistent
testing environment. Don't forgo real API testing once you've got past your prototyping
stage. You should place these test fixtures in test/fixtures
.
var fixtures = [
'test/fixtures/example-geolocation.json'
];
Now set up the test harness with any configs or fixtures that it needs. The help_text
here would be available inside your app as the im.config.help_text
variable. When you launch
this app on live you would need to ensure that you created a config
key with this as json to
make your app behave the same. async: true
ensures that an Promises you use are executed
in a way that mimics the really asynchronous behaviour of the live system.
var tester = new vumigo.test_utils.ImTester(app.api, {
custom_setup: function (api) {
api.config_store.config = JSON.stringify({
help_text: "Sorry, something went wrong. Try again."
});
fixtures.forEach(function (f) {
api.load_http_fixture(f);
});
},
async: true
});
Now we get to the important stuff, your tests! Write lots!
A test consists of a description and a check_state
function with the following structure: check_state(user, content, next_state, response, continue_session)
user: users state dict (null if first state)
content: user input (numerical key they press or text they enter for free text screens)
next_state: expected state (state_name)
expected_response: what should come back (regex, remember to escape control characters like ? with two backslashes like: \?)
continue_session: will the next_state be an end state?
Here's your first test, which if you're creating a USSD app should always start with both user
content
as null
because the users haven't started interacting with it yet.
it("first screen should ask us to say something ", function (done) {
var p = tester.check_state({
user: null,
content: null,
next_state: "first_state",
response: "^Say something please",
continue_session: true
});
p.then(done, done);
});
In this next example the user is already interacting with the app. current_state
is where we enter the app, answers
is a dict of results of previous states we're mocking having completed. The answers values should be the result of the user pressing the numerical keys, not the key value (e.g. "Hello world!" not "1").
it("declining to know what we said, should say goodbye", function (done) {
var user = {
current_state: 'second_state',
answers: {
first_state: 'Hello world!'
}
};
var p = tester.check_state({
user: user,
content: "2",
next_state: "end_state",
response: "^Thank you and bye bye!$",
continue_session: false
});
p.then(done, done);
});
Finally close off the grouping of tests. You can create multiple groupings (useful if you have a bunch of different tests for unregistered and registered users).
});
You can run these tests by running:
$ npm test
or if you like to see the spec output with it running as a watcher so you can code away with the tests running try:
$ ./node_modules/.bin/mocha -R spec --watch
Here is the test file in complete form:
var fs = require("fs");
var assert = require("assert");
var vumigo = require("vumigo_v01");
var app = require("../lib/vumi-go-skeleton");
// This just checks that you hooked you InteractionMachine
// up to the api correctly and called im.attach();
describe("test_api", function() {
it("should exist", function() {
assert.ok(app.api);
});
it("should have an on_inbound_message method", function() {
assert.ok(app.api.on_inbound_message);
});
it("should have an on_inbound_event method", function() {
assert.ok(app.api.on_inbound_event);
});
});
// YOUR TESTS START HERE
// CHANGE THIS to test_your_app_name
describe("When using the skeleton", function() {
// These are used to mock API reponses
// EXAMPLE: Response from google maps API
var fixtures = [
'test/fixtures/example-geolocation.json'
];
var tester = new vumigo.test_utils.ImTester(app.api, {
custom_setup: function (api) {
api.config_store.config = JSON.stringify({
//user_store: "go_skeleton"
});
fixtures.forEach(function (f) {
api.load_http_fixture(f);
});
},
async: true
});
// first test should always start 'null, null' because we haven't
// started interacting yet
it("first screen should ask us to say something ", function (done) {
var p = tester.check_state({
user: null,
content: null,
next_state: "first_state",
response: "^Say something please",
continue_session: true
});
p.then(done, done);
});
it("declining to know what we said, should say goodbye", function (done) {
var user = {
current_state: 'second_state',
answers: {
first_state: 'Hello world!'
}
};
var p = tester.check_state({
user: user,
content: "2",
next_state: "end_state",
response: "^Thank you and bye bye!$",
continue_session: false
});
p.then(done, done);
});
});