Overview
This tutorial walks through the steps need to configure Karma to work with a Maven web project. It will also be used as a base for my future Karma related posts.
Install Node.js
First, download Node.js from http://nodejs.org/download/ and install it. Once installed, we should be able to invoke npm
command from the command line, for example:-
npm -version
Create package.json
Let’s assume we have the following Maven project:-
testKarma ├── pom.xml ├── src │ ├── main │ │ ├── java │ │ ├── resources │ │ └── webapp │ └── test │ └── java └── testKarma.iml
Please ignore testKarma.iml
, which is an IntelliJ specific file.
Create a file called package.json
under project root directory…
testKarma ├── package.json ├── pom.xml ├── src │ ├── main │ │ ├── java │ │ ├── resources │ │ └── webapp │ └── test │ └── java └── testKarma.iml
… with the following content…
{ "name": "testKarma", "private": true }
In this case, we created a private repository by setting private
to true
.
Without this flag set, when you run npm install ...
, we will get these annoying warnings:-
npm WARN package.json testKarma@ No description npm WARN package.json testKarma@ No repository field. npm WARN package.json testKarma@ No README data
Install Karma and Plugins
From the project root directory, run the following command:-
npm install karma karma-jasmine@0.2.2 karma-chrome-launcher karma-phantomjs-launcher karma-junit-reporter karma-coverage --save-dev
At the time of this post, by default, karma-jasmine
will install version 0.1.5. So, we will manually specify the latest version that allows us to use Jasmine 2.x.
For local testing, we will run our tests against both Chrome browser and PhantomJS, which is a headless browser. So, make sure Chrome browser is already installed.
The project structure now contains the installed plugins for JavaScript testing:-
testKarma ├── node_modules │ ├── karma │ ├── karma-chrome-launcher │ ├── karma-coverage │ ├── karma-jasmine │ ├── karma-junit-reporter │ └── karma-phantomjs-launcher ├── package.json ├── pom.xml ├── src │ ├── main │ └── test └── testKarma.iml
The package.json
contains a history of the plugins we installed within this project:-
{ "name": "testKarma", "private": true, "devDependencies": { "karma": "^0.12.24", "karma-chrome-launcher": "^0.1.5", "karma-coverage": "^0.2.6", "karma-jasmine": "^0.2.2", "karma-junit-reporter": "^0.2.2", "karma-phantomjs-launcher": "^0.1.4" } }
A couple of important notes:-
- Basically, since we are not running
npm install
with-g
option, the plugins will not be installed globally. Rather, they are installed within the project root directory. - Using
--save-dev
option, ifpackage.json
exists, this file will be automatically updated to keep track of the installed plugins and versions. - If we decided to update the plugin versions, we just need to modify this file and run
npm install
to update them. - We do not want to commit
node_modules
directory into any VCS because there are at least 5000 files in this directory. So, remember to configure a VCS exclusion on this directory (see IntelliJ: Handling SVN Global Ignore List). - When other peers check out this project from VCS, they will run
npm install
, which will automatically install all the dependencies listed inpackage.json
.
Create Karma Configuration File
Instead of running karma init karma.conf.js
to step through the process to create a configuration file, we will manually create two Karma configuration files under src/test/resources
directory.
testKarma ├── node_modules ├── package.json ├── pom.xml ├── src │ ├── main │ │ ├── java │ │ ├── resources │ │ └── webapp │ └── test │ ├── java │ └── resources │ ├── karma.conf.js │ └── karma.jenkins.conf.js
karma.conf.js
This configuration is used for local testing.
module.exports = function ( config ) { config.set( { basePath : '../../../', frameworks : ['jasmine'], files : [ 'src/main/webapp/resources/js/**/*.js', 'src/test/js/**/*.js' ], exclude : [], preprocessors : { 'src/main/webapp/resources/js/**/*.js' : ['coverage'] }, reporters : ['progress', 'coverage'], port : 9876, colors : true, logLevel : config.LOG_INFO, autoWatch : true, browsers : ['Chrome', 'PhantomJS'], singleRun : false, plugins : [ 'karma-jasmine', 'karma-chrome-launcher', 'karma-phantomjs-launcher', 'karma-junit-reporter', 'karma-coverage' ], coverageReporter : { type : 'html', dir : 'target/coverage/' } } ); };
karma.jenkins.conf.js
This configuration is used for automated testing on Jenkins.
module.exports = function ( config ) { config.set( { basePath : '../../../', frameworks : ['jasmine'], files : [ 'src/main/webapp/resources/js/**/*.js', 'src/test/js/**/*.js' ], exclude : [], preprocessors : { 'src/main/webapp/resources/js/**/*.js' : ['coverage'] }, // added `junit` reporters : ['progress', 'junit', 'coverage'], port : 9876, colors : true, logLevel : config.LOG_INFO, // don't watch for file change autoWatch : false, // only runs on headless browser browsers : ['PhantomJS'], // just run one time singleRun : true, // remove `karma-chrome-launcher` because we will be running on headless // browser on Jenkins plugins : [ 'karma-jasmine', 'karma-phantomjs-launcher', 'karma-junit-reporter', 'karma-coverage' ], // changes type to `cobertura` coverageReporter : { type : 'cobertura', dir : 'target/coverage-reports/' }, // saves report at `target/surefire-reports/TEST-*.xml` because Jenkins // looks for this location and file prefix by default. junitReporter : { outputFile : 'target/surefire-reports/TEST-karma-results.xml' } } ); };
Write JS Production Code and Test Code
Now, it’s time to write some tests and run them.
testKarma ├── node_modules ├── package.json ├── pom.xml ├── src │ ├── main │ │ ├── java │ │ ├── resources │ │ └── webapp │ │ └── resources │ │ └── js │ │ └── hello.js │ └── test │ ├── java │ ├── js │ │ └── hello-spec.js │ └── resources │ ├── karma.conf.js │ └── karma.jenkins.conf.js └── testKarma.iml
hello.js
We will keep our production code to the minimum in this example:-
var hello = { speak : function () { return 'Hello!'; } };
hello-spec.js
A very simple test case:-
describe( 'hello module', function () { 'use strict'; it( 'speak()', function () { expect( hello.speak() ).toBe( 'Hello!' ); } ); } );
Run Karma using karma.conf.js
From the project root directory, run Karma:-
node_modules/karma/bin/karma start src/test/resources/karma.conf.js
The console should now look like this:-
INFO [karma]: Karma v0.12.24 server started at http://localhost:9876/ INFO [launcher]: Starting browser Chrome INFO [launcher]: Starting browser PhantomJS INFO [PhantomJS 1.9.8 (Mac OS X)]: Connected on socket YNMziWTsyeaf6DZzw06D with id 3320523 INFO [Chrome 38.0.2125 (Mac OS X 10.9.5)]: Connected on socket rYQeni1xm1bbqfa3w06E with id 50259203 PhantomJS 1.9.8 (Mac OS X): Executed 1 of 1 SUCCESS (0.003 secs / 0.001 secs) Chrome 38.0.2125 (Mac OS X 10.9.5): Executed 1 of 1 SUCCESS (0.008 secs / 0.001 secs) TOTAL: 2 SUCCESS
The target/coverage
directory should contain Chrome and PhantomJS subdirectories.
testKarma ├── node_modules ├── package.json ├── pom.xml ├── src ├── target │ └── coverage │ ├── Chrome 38.0.2125 (Mac OS X 10.9.5) │ │ ├── index.html │ │ ├── js │ │ │ ├── hello.js.html │ │ │ └── index.html │ │ ├── prettify.css │ │ └── prettify.js │ └── PhantomJS 1.9.8 (Mac OS X) │ ├── index.html │ ├── js │ │ ├── hello.js.html │ │ └── index.html │ ├── prettify.css │ └── prettify.js └── testKarma.iml
When opening one of the index.html
files, we should see the coverage report.
Run Karma using karma.jenkins.conf.js
Although we will only use karma.jenkins.conf.js
for automated testing on Jenkins, we will run this configuration file to see the generated output differences.
Clean the target
directory:-
mvn clean
Run Karma with karma.jenkins.conf.js
instead:-
node_modules/karma/bin/karma start src/test/resources/karma.jenkins.conf.js
Since this configuration only runs on the headless browser, we will not see Chrome results here:-
INFO [karma]: Karma v0.12.24 server started at http://localhost:9876/ INFO [launcher]: Starting browser PhantomJS INFO [PhantomJS 1.9.8 (Mac OS X)]: Connected on socket SM_5sy2wzHdL4ru9yg0X with id 42922522 PhantomJS 1.9.8 (Mac OS X): Executed 1 of 1 SUCCESS (0.002 secs / 0.001 secs)
The target
directory should contain cobertura-coverage.xml
for coverage result and TEST-karma-results.xml
for test result.
testKarma ├── node_modules ├── package.json ├── pom.xml ├── src ├── target │ ├── coverage-reports │ │ └── PhantomJS 1.9.8 (Mac OS X) │ │ └── cobertura-coverage.xml │ └── surefire-reports │ └── TEST-karma-results.xml └── testKarma.iml