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.
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).
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.
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.