Karma testing with code coverage for Oracle JET part 1.

1

This is the first blog in a series of two blogs about Karma testing (with code coverage) for Oracle JET. This first blog will help you with setting up the files in the project to get karma up and running. The second blog can be found here and will show you how to actually test your code in the viewModels.

Start a new project.

First we start our new Oracle JET project following the example on the Oracle JET website.

npm install -g bower grunt grunt-cli yo
npm install generator-oraclejet
yo oraclejet ojet-karma-test --template=navdrawer

Now check if your project is working by running:

grunt build
grunt serve

Next step is to add all the npm modules for karma, jasmine and supporting coverage reports.

npm install -g karma karma-cli
npm install karma karma-jasmine jasmine-core karma-coverage --save-dev

Add a directory named test, this is where the testfiles will be. Add a file named test-main.js in the directory and leave it empty for now.

Karma Configuration.

Go back to the main directory of your project. Next step is to make the configurations for karma, I do this using

karma init

The framework we are using is Jasmine, we do need the Require.js plugin so answer yes for that question. I use Chrome as a browser but you can pick whatever browser you like. All the other questions I left empty or answered with “no”.

Open the karma.conf.js and add the libraries, viewModel, testfiles and the test-main file to files.

    files: [
      {pattern: 'js/libs/**/*.js', included: false},
      {pattern: 'src/js/viewModels/*.js', included: false},
      {pattern: 'test/*Spec.js', included: false},
      'test/test-main.js'
    ],

Exclude the main.js file

exclude: [
      'src/js/main.js',
    ],

To have karma coverage replace the preprocessor with:

 preprocessors: {
      'src/js/viewModels/*.js':['coverage']
    },

Add a coverage reporter.

   coverageReporter:{
       type:'html',
       dir:'reports/coverage'
    },

And add coverage to the reporter:

reporters: ['progress','coverage'],

Your karma config will now look like this:


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

    basePath: '',

    frameworks: ['jasmine', 'requirejs'],

    files: [
      {pattern: 'src/js/libs/**/*.js', included: false},
      {pattern: 'src/js/viewModels/*.js', included: false},
      {pattern: 'test/*Spec.js', included: false},
       'test/test-main.js'
    ],
    exclude: [
      'src/js/main.js',
    ],
    preprocessors: {
      'src/js/viewModels/*.js':['coverage']
    },

    reporters: ['progress','coverage'],
    coverageReporter:{
       type:'html',
       dir:'reports/coverage'
    },
    port: 9876,
    colors: true,
    logLevel: config.LOG_INFO,
    autoWatch: false,
    browsers: ['Chrome'],
    singleRun: true,
    concurrency: Infinity
  })
}

Setting up test-main.js

Let’s head back to our test-main.js, this is where the Karma Require.JS magic happens, this is where we load the modules needed for testing.

var tests = [];
var modules = [];
var SPEC_REGEXP = /Spec\.js$/;
var VIEWMODEL_REGEXP = /viewModels\//;
var JS_REGEXP = /\.js$/;

var jsToModule = function (path) {
    return path.replace(/^\/base\/src\/js\//, '').replace(/\.js$/, '');
};

for (var file in window.__karma__.files) {
  if (window.__karma__.files.hasOwnProperty(file)) {
    if (SPEC_REGEXP.test(file)) {
      tests.push(file);
    }
    else if (VIEWMODEL_REGEXP.test(file) && JS_REGEXP.test(file)) {
        modules.push(jsToModule(file));
    }
  }
}

var startTest= function(){
  //Load the modules before calling karma start.
  require(modules, function () { 
    window.__karma__.start()
  })
}

requirejs.config({
    baseUrl: '/base/src/js',

 paths:
  {
     'knockout': 'libs/knockout/knockout-3.4.0.debug',
    'jquery': 'libs/jquery/jquery-3.1.0',
    'jqueryui-amd': 'libs/jquery/jqueryui-amd-1.12.0',
    'promise': 'libs/es6-promise/es6-promise',
    'hammerjs': 'libs/hammer/hammer-2.0.8',
    'ojdnd': 'libs/dnd-polyfill/dnd-polyfill-1.0.0',
    'ojs': 'libs/oj/v2.1.0/debug',
    'ojL10n': 'libs/oj/v2.1.0/ojL10n',
    'ojtranslations': 'libs/oj/v2.1.0/resources',
    'text': 'libs/require/text',
    'signals': 'libs/js-signals/signals'
  },

  shim:
  {
    'jquery':
    {
      exports: ['jQuery', '$']
    }
  },
    deps: tests,
    callback: startTest
});

This is a bit more elaborate than the standard test-main file used for Require.JS and Karma. The modules that we are testing will be loaded by Require.JS in the testfiles. We use the StartTest function to load the modules where we want test coverage on (everything in the viewmodel folder in this case) with Require.JS before the testing start. If we do not do this, we will not get coverage on files that do not have a test file. 

Run karma.

Time to see if karma is running!
Run the following in the terminal / console:

karma start

When the tests are done there should be a folder called reports in your project. Open de index.html to view the code coverage of your viewModels folder. Click on the links to see more detailed information about which code is covered and which is not.
coverage-example

The code mentioned in this blog can also be found on my Github Account, with a bit more comments in the code to clear things up.
Part two is available as well: Part two

Resources:
http://www.bradoncode.com/blog/2015/02/27/karma-tutorial/
https://karma-runner.github.io/0.8/plus/RequireJS.html
http://stackoverflow.com/questions/26850684/karma-coverage-requirejs-misleading-coverage-reports

About Author

Cindy studied Software Engineering in Amsterdam. After university she starting working as an ADF developer at AMIS. Then webdevelopment happened and has since been the main focus. When not coding she is stopping balls as a goalkeeper.

1 Comment

  1. Dimitris Veneros on

    Hi,
    I am getting 404 error for this pattern:
    ‘src/js/libs/**/*.js’

    This makes sense since there is no libs folder under /src/js/ directory.
    Am I missing something?

    Thanks

Leave a Reply

This site uses Akismet to reduce spam. Learn how your comment data is processed.