Skip to content
This repository has been archived by the owner on Jun 12, 2018. It is now read-only.

Writing tests

Mike Jones edited this page May 29, 2013 · 1 revision

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

Happy testing!

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);
    });
});
Clone this wiki locally