Phantom Mochachino is an open source functional testing utility written in Javascript.

It harnesses the power of PhantomJS, a headless web browser based off of Webkit, and the MochaJS Javascript testing framework.

The Problem

Testing the functionality of a website.

Unit testing the backend, and front end of a website is relatively easy. Testing the functionality of a website in a manner that is remotely comparable to how a real user would interact with it is somewhat more difficult.

The solution

PhantomJS provides the best solution as regards a headless web browser with an easily manipulatable programmable interface.

MochasJS provides an extremely expressive behavioural driven development interface that is intuitive to use.

Phantom Mochachino interfaces with PhantomJS to inject and run Mocha based tests on a specified web page. It also extends Mocha's BDD interface to provide expressive methods for informing the test framework of impending page changes. This allows you to manipulate your webpages using any tools that you see fit, for example jQuery.

Getting started

The below example is a test script for testing a login form.

The basic test syntax is the Mocha syntax. Phantom Mochachino is an extension of Mocha - it does not touch the Mocha source code - an intentional design decision.

describe('Example Test Suite', function(){

    describe('Test the form', function(){

    	it('starts with empty inputs', function(){
            var $username = $("#username"),
                $password = $("#password");

    		expect($username.val()).to.equal('');
    		expect($password.val()).to.equal('');
    	});


        changeThePage('by clicking the login button', function(done) {
            doClick();
            done();
        });

    });

    describe('Test the results', function(){

        it('logged in successfully', function(){
            var $tag = $("#login-success");

            expect($tag.length).to.equal(1);
        });

        markAsFinalSuite();
    });
});

Phantom Mochachino provides additional methods, namely: changeThePage(title, fn), submitTheForm(title, fn), testAnAlternatePath(), and markAsFinalSuite().

changeThePage(title, fn)

The parameter fn is a function which when executed will cause the page to change. For example it may utilize Javascript to click a link.

submitTheForm(title, fn)

The parameter fn is a function which when executed will cause the page to change. For example it may utilize Javascript to submit a form. This method is an alias of changeThePage(title, fn) - the intention is to make your test code more concise and clear.

testAnAlternatePath()

Call this method to indicate that the test suites after this one are written with the intention of testing a different path.

This will trigger a page reload after which the suites that follow this call will be executed.

Use this if for example you want to verify that multiple different things happen when you do different things on the same page. For example to test tha:

  1. A login form logs in successfully when the correct inputs are passed.

  2. Error messages are shown when incorrect inputs are passed.

markAsFinalSuite()

Use this to indicate to Phantom Mochachino that this suite is the final suite.

Running the tests

You run your tests utilizing phantomjs on the command line.

Included in the Phantom Mochachino source is a test runner named FunctionalTestRunner.js.

You run this script passing in the name of the test file, and the target URL on which you want the tests to be run.

For example:

phantomjs FunctionalTestRunner.js YourTestFile.js website/path

Before you execute such a command, you need to set up your configuration in phantom-mochachino-config.json. This is where you tell Phantom Mochachino where to find your instance of Mocha and your assertion library (amongst other things).

Advanced Usage

Cookies

Sometimes usage of a website considers state. For example user login state. If you want to test a piece of functionality which requires a user to be logged in, then you must be able to maintain state across test executions.

PhantomJS (1.9) does support cookies, but annoyingly by default it removes session cookies after each run. You can fix this with relative ease. Simply modify cookiejar.cpp within the PhantomJS source, commenting out the call to purgeSessionCookies(). Then build PhantomJS from source.

CookieJar::~CookieJar()
{
    // On destruction, before saving, clear all the session cookies
    //purgeSessionCookies();
    save();
}

State across tests

Phantom Mochachino was built to solve a problem. One part of the problem that it did not initially solve was state across tests.

For example if you want to test a user 'like' button.

Once a user has 'liked' something they cannot like it again. As such if we hard code our user within our test code, we can only run our test once - the next time it will fail.

To resolve this, the Phantom Mochachino test runner will pass any additional arguments that you use when calling phantomjs on the command line to your test scripts.

If for example you call:
phantomjs FunctionalTestRunner.js YourTestFile.js website/path username password

then 'username' and 'password' will be accessible from within your tests using mochachino.injectedArgs[0] and mochachino.injectedArgs[1] respectively.

For my initial use case, I test my registration path, then my login path, and then the paths which require the user to be logged in. I use these injected arguments across all these tests so I know that on each execution I have a 'clean slate'.

Additional Reading

I have written a blog post entitled outlining the script that I have written to control my functional testing.

Additional Comments

If you have any suggestions for further development of Phantom Mochachino, I would be interested to here them. Please do feel free to contribute, and get involved.

Hopefully you find this useful :)