ti-mocha

Simple and reliable support for mocha testing with Appcelerator's Titanium SDK

Download .zip Download .tar.gz View on GitHub

Table of Contents

Installation

Easy Way

cd /path/to/Titanium/project && npm install ti-mocha --prefix ./node_modules

If executed in your project's root folder, this will put ti-mocha.js in Resources for traditional Titanium apps, or app/lib for Alloy apps.

Less Easy Way

  1. Download ti-mocha.js.
  2. Copy ti-mocha.js into your project's Resources folder (or app/lib if you're using alloy).

Basic Usage

// creates the "mocha" global necessary to run a test suite anywhere in your app
require('ti-mocha');

// create the test suite
describe('ti-mocha', function() {

    describe('suite 1', function() {

        it('shows passing tests (fast)', function(){});

        it('shows passing tests (slow)', function(done){
            setTimeout(done, 1500);
        });

    });

    describe('suite 2', function() {

        it('shows pending tests');

        it('fails a test', function() {
            throw new Error('this shoud fail');
        });

    });

});

// run the tests
mocha.run();

Reporters

  • ti-spec - Titanium-compatible version of the original mocha spec reporter
  • ti-spec-studio - Same as ti-spec, but this one is optimized for use in Titanium Studio
  • ti-json - Titanium-compatible version of the original mocha json reporter

The ti-spec reporter is chosen by default. To switch to a different reporter, use mocha's setup() function before run your test suites. Original mocha reporters upon which these reporters are based can be found here.

mocha.setup()

There's few new properties you can add via mocha.setup() to configure your test runs.

var outputFile = Ti.Filesystem.getFile(Ti.Filesystem.tempDirectory, 'results.json');
outputFile.createFile();

mocha.setup({ 
    reporter: 'ti-json',    // the reporter to use with your tests
    outputFile: outputFile, // write results to the given Ti.Filesystem.File file
    quiet: true             // if true, suppress all console logging
});

mocha.run()

This will kick off your tests. Additionally, you can give it a callback argument to be executed once the tests complete.

var runner = mocha.run(function() {
    // print the stats from the runner after the test completes
    console.log(JSON.stringify(runner.stats));
});

Note for reporter developers

Giving your reporter the suffix "-studio" will automatically modify ti-mocha's underlying console.log() implementation to exclude ANSI code sequences (covered in Caveats). This helps to automatically optimize your test output for the Titanium Studio console.

Using should.js with ti-mocha

While mocha doesn't care what you use as your assertion library, it works particularly well with should.js, not coincidentally due to them having the same author. To install should.js:

  1. Download should.js.
  2. Copy should.js into your project's Resources folder.

For full documentation on what should.js can do for your tests, check out the documentation.

Titanium + mocha + should example

app.js

require('ti-mocha');

// create a basic UI
var win = Ti.UI.createWindow({
    backgroundColor: '#fff',
    fullscreen: false,
    exitOnClose: true,
    id: 'myWindow'
});
var view = Ti.UI.createView({
    height: Ti.UI.FILL,
    width: Ti.UI.FILL,
    backgroundColor: '#a00',
    id: 'myView'
});
win.add(view);

// run tests after window opens to ensure UI is initialized
win.addEventListener('open', function() {
    require('test/app_test')(win, view);
});

// show the UI
win.open();

test/app_test.js

var should = require('should');

module.exports = function(win, view) {

    describe('app.js', function() {

        describe('#myWindow', function() {

            it('exists', function() {
                should.exist(win);
                win.id.should.equal('myWindow');
            });

            it('has Ti.UI.Window functions', function() {
                should(win.open).be.a.Function;
                should(win.close).be.a.Function;

                if (Ti.Platform.name === 'iPhone OS') {
                    should(win.hideTabBar).be.a.Function;
                }
            });

            it('has dimensions equal to the device', function() {
                win.size.height.should.equal(Ti.Platform.displayCaps.platformHeight);
                win.size.width.should.equal(Ti.Platform.displayCaps.platformWidth);
            });

        });

        describe('#myView', function() {

            it('exists', function(){
                should.exist(view);
                view.id.should.equal('myView');
            });

            it('has Ti.UI.View functions', function() {
                should(view.add).be.a.Function;
            });

            it('is a child of window', function() {
                win.children.length.should.equal(1);
                should.exist(win.children[0]);
                win.children[0].id.should.equal('myView');
            });

            it('view has same dimensions as window', function(){
                view.size.height.should.equal(win.size.height);
                view.size.width.should.equal(win.size.width);
            });

        });

    });

    // run the tests
    mocha.run();
};

output

example output

Tips

  • Set your log level to info to get the best looking test results.
  • .jshintrc for Titanium + mocha + should
  • If you're going to test Titanium UI state (like size and position), be sure to wait until the UI is ready. The easiest way to do this in most cases is to wait for the containing Ti.UI.Window to be open.
var win = Ti.UI.createWindow();

// assemble the rest of "win" UI

win.addEventListener('open', function() {
    // run tests against "win" or its children
});
win.open();

Caveats

These caveats include tips for usage, as well as for development of your own Titanium-compatible reporters.

Titanium proxies don't play well with should.js

should.js works by extending Object.prototype with the should object. While this works for all pure Javascript objects and most Titanium proxy properties, it does not update the proxies themselves, or their functions. When attempting to use should directly on Titanium proxies or their functions, you must wrap the proxy or function in a should call to avoid a runtime undefined error.

var should = require('should');

var win = Ti.UI.createWindow();
win.open.should.be.a.Function;  // will throw a runtime error
should(win.open).be.a.Function; // will work as expected

Ti.API calls and Studio Console don't like ANSI codes

Much of the reporters in mocha, including ti-spec which is optimized for the terminal, make use of ANSI codes for the test reporting output. These are integral to coloring, as well as controlling the cursor in mocha's reporters. Unfortunately, Titanium Studio's console does not handle ANSI codes, either ignoring them or printing them as literals. On top of this, the Ti.API calls will often mangle commonly used ANSI characters, like or . For this reason, when constructing your own reporters, it's wise to use standard characters or test heavily for compatibility before using any ANSI codes.

Ti.API calls trim leading whitespace

For example, this:

Ti.API.info('    this should have 4 leading spaces');

will print as this (notice the lack of leading spaces):

this should have 4 leading spaces

In order to workaround this, we can take advantage of the previously mentioned caveat. If you wrap your whitespace in an ignored ANSI code, the whitespace will be preserved. So the above code would print with the 4 leading spaces if you modified it to look like this:

// color() helper from Base reporter class
var color = require('./base').color;

Ti.API.info(color('suite', '    ') + 'this should have 4 leading spaces');

// or you can simply include the whitespace in the coloring of the whole line

Ti.API.info(color('suite', '    this should have 4 leading spaces'));

Links