Joel's Thoughts

Marionette Backbone App Unit Test Using Karma

September 26, 2016

So what is Karma? Karma is a test runner for Javascript where you can run or invoke unit tests using a terminal or CLI.

Check-out Karma’s website to learn more.

I’m starting a personal project using Marionette Backbone framework and RequireJs on the frontend calling some RESTful APIs. This is how I setup Karma to run Jasmine Unit Test cases for the Marionette view.

My frontend folder structure looks like this (checkout on Github):

For a start, I created a unit test on one of my views located at “app/views/index”.

File page.js holds the actual code of the Marionette view and page.spec.js holds the Unit Test code. This is how page.spec.js looks like:


define(["../../views/index/page", "jquery", "backboneMarionette"], function (LoginView, $, Marionette) {

  describe("LoginView Test", function () {

    var testApp, widget, regionId = "#test-region";

    beforeEach(function () {

      /**
      * @Todo Extract the initialization part to be global
      */
      var fixture = '<div id="fixture">';
      fixture += '<div id="test-region"></div>' +
        '</div>';


      document.body.insertAdjacentHTML(
        'afterbegin',
        fixture);

      testApp = new Marionette.Application();

      testApp.addRegions({
        region: regionId
      });

      testApp.addInitializer(function () {        
        widget = new LoginView(); //initialize the actual view to code

        Backbone.Marionette.TemplateCache.prototype.loadTemplate = function (templateId, callback) {
          var template = templateId; //pertains to the path of the template         
          if (!template || template.length === 0) {
            var msg = "Could not find template: '" + templateId + "'";
            var err = new Error(msg);
            err.name = "NoTemplateError";
            throw err;
          }
          return template;
        };

        testApp.region.show(widget);
      });

      testApp.start();
    });

    afterEach(function () {
      document.body.removeChild(document.getElementById('fixture'));
    });

    it("Should set model's attributes with valid values.", function () {     
      $('input[name=email]').val("testdfsf@gmail.com");
      $('input[name=password]').val("testdfsfs");
      $('button[name=sign-in]').click();

      expect(widget.model.attributes.email.length > 0 && widget.model.attributes.password.length > 0).toBeTruthy();
    });

    ..... more code

  });//end of the describe function

});//end of the define function 

If you notice, I’m appending a test fixture element when the test starts to run. Inside the fixture element is a div tag with an id of test-region. This is where the view will be mounted during the test.

Since this is using RequireJS as the file/module loader, the main.js is located in the root of the app/js folder.

Test version of the main.js

Also, I need to create a test version of the main.js to run the unit test. It’s located inside the test folder.

This is how it looks like (full version of the test-main.js on Github):

var tests = [];
for (var file in window.__karma__.files) {
    if (/spec\.js$/.test(file)) {
        tests.push(file);
    }
}

requirejs.config({

    baseUrl: "/base/js",
    paths: {

        /* jquery + jquery-ui + jquery-plugins*/
        jquery: [
            'https://ajax.googleapis.com/ajax/libs/jquery/1.8.0/jquery.min',
            '../app/js/libs/jquery/jquery-1.8.0.min'
        ],
        app: '../app/js/app',
        /* underscore */
        underscore: '../app/js/libs/underscore/underscore',
        underscoreString: '../app/js/libs/underscore/underscore.string',

        ......more code between here

        //added 
        dummyId: '../app/js/libs/utils/dummyId', //loads a dummy id as an initial identity for user
        layout: '.../app/js/libs/utils/layout',
        appConfig: '../app/js/config/app-config'

      },

    shim: {
         backbone: {
            exports: 'Backbone',
            deps: ['jquery', 'underscore']
        },
        backboneMarionette: {
            exports: 'Backbone.Marionette',
            deps: ['backbone']
        },
        backboneBUI: {
            deps: ['backbone']
        },       
        underscore: {
            exports: '_'
        },

        //tests
         backboneValidation: {
            deps: ['backbone']
        },
        backboneValidationBootstrap: {
            deps: ['backbone', 'backboneValidation']
        },
        serialize: {
           deps: ['jquery']
        }
    },

    // dynamically load all test files
  deps: tests,

  // we have to kickoff jasmine, as it is asynchronous
  callback: window.__karma__.start

});

It’s important to notice that “baseUrl” is set to “base/js” since Karma serves file under the base folder. As you notice, all the file path inside test-main.js is relative to the base url ( ex. ../app/js/config/app-config).

The karma.conf.js

Karma.conf.js holds the configuration values for karma. This is how I configure the values:

// Karma configuration
// Generated on Sun Sep 25 2016 16:08:03 GMT-0700 (Pacific Daylight Time)

module.exports = function(config) {
  config.set({

    // base path that will be used to resolve all patterns (eg. files, exclude)
    basePath: '',


    // frameworks to use   
    frameworks: ['jasmine', 'requirejs'],


    // list of files / patterns to load in the browser
    files: [
        //include all the files with extension .js inside the app folder
        {pattern: 'app/js/**/*.js', included: false},
        //include all the html file inside templates folder
        {pattern: 'app/templates/**/*.html', watched: false, included: false, served: true, nocache: false},
        'test/test-main.js'     
    ],


    // list of files to exclude
    exclude: [
      //do not include the main.js since we have the test-main.js to act on its behalf
      'app/js/main.js'
    ],


    //uncomment to run html reporter
    reporters: ['progress','html'],

    htmlReporter: {
      outputFile: 'test/units.html',            
      // Optional 
      pageTitle: 'Unit Tests',
      subPageTitle: 'A sample project description',
      groupSuites: true,
      useCompactStyle: true,
      useLegacyStyle: true
    },


    // web server port
    port: 9876,


    // enable / disable colors in the output (reporters and logs)
    colors: true,


    // level of logging
    // possible values: config.LOG_DISABLE || config.LOG_ERROR || config.LOG_WARN || config.LOG_INFO || config.LOG_DEBUG
    logLevel: config.LOG_INFO,


    // enable / disable watching file and executing tests whenever any file changes
    autoWatch: true,


    // start these browsers
    // available browser launchers: https://npmjs.org/browse/keyword/karma-launcher
    browsers: ['Chrome'],


    // Continuous Integration mode
    // if true, Karma captures browsers, runs the tests and exits
    singleRun: false,


  });
};

The configuration starts with the list of frameworks to use. I specified “jasmine” and “requirejs”. Take note that I included all html files inside the templates folder. Since I’m using requirejs to load the html templates on my Marionette views, I also need to load them on the browser during test performance.

Also I’m using an html reporter. This will create an html file which contains results of the Unit Test. The file is configured to be created inside test folder.

Run Karma

Now to run karma on the CLI, I need to do this command:

 karma start

The command will run the test and open a new window of Google Chrome browser.

With this set-up, I can now start writing unit test while building other components of my app and see them pass or fail in real-time. It’s very convenient and will save me a lot of time.









  • About
  • Search
  • Resume
  • Powered by Jekyll using the Trio theme