Testing Marionette Application Modules

Why is it that everything in Backbone and Marionette has extend() except for Marionette.Application? Derick Bailey is one of my programming heroes but I feel like he may have dropped the ball here. Why? Well… what if you wanted to have multiple applications but reuse the same modules? You can’t. Modules can only be attached to a specific instance of Marionette.Application. This is not very testable because you are always working on a single instance of your Marionette App and cannot test in isolation.

So I recently wrote a plugin to allow this. https://github.com/kdocki/marionette.application.module

Once I’ve loaded the module plugin above I could create new apps on each test setUp in karma or jasmine-runner without having to worrying about the global events namespace being polluted or modules conflicting. In my test.js I created an app1 and app2, both will be completely separate applications and both have the Foo module included. Neat, right?

This is the test.js

var app1 = createApplication();
var app2 = createApplication();

app1.Foo.bar = 3;
app2.Foo.bar = 4;

app1.Foo.bar === app2.Foo.bar || throw "Bah!";

// yay! this works correctly now

Here is a little more to this puzzle in case you are wondering what createApplication() is doing.

var App = Marionette.Application.extend();

function createApplication()
{
    var app = new App;

    // set your initialize before and initialize after,
    // plus any application globals you want on your object

    return app;
}

Next we create a Foo module for our Application.

App.module("Foo", function (Foo, App, Backbone, Marionette, $, _)
{
    var API =
    {
        showFoo : function(fooId)
        {
            App.execute('dispatcher:set:route', 'show:(id)', fooId);
            Foo.controller = new Foo.Show.Controller();
            Foo.controller.show(fooId);
        }
    };

    Foo.Router = Marionette.AppRouter.extend(
    {
        appRoutes: {
            'show/:id'      : 'showFoo'
        }
    });

    App.addInitializer(function()
    {
        return new Foo.Router({ controller: API });
    });

    App.commands.setHandler('!route:foo:show', API.showFoo);

});

Testing in isolation is important because it leaves us with our sanity. It is nice to be able to reset the state of the application anytime, i.e. in Ember you do App.reset(); Marionette should do this out of the box, maybe Derick can make it happen? It’d be a nice feature to have!

post by K.D. on 11/26/2013