Blog
SubscribeSubscribe

How to Create a Custom Karma reporter

So, you wanted to run unit tests on your JavaScript client code, and you decided to use Karma test runner.

You wrote the tests, made the proper configurations and everything was OK.

But then you find out, that viewing the results in the default reporter is not enough for your needs. You want to do something else with the results.

You search npm for Karma reporters and maybe you reach this list.

You go through the list and find there many interesting options, like creating an HTML report, or creating a JUnit report, that enables you to view your test results in Bamboo CI engine, and many more.

However, what you need is not there.

For me, it was writing to a mongoDB database. For you, it could be representing the test data in a pie chart, or sending an SMS whenever a test fails.

Yes, you could use an existing reporter and afterward parse the data and do whatever you want with it, but it produces unnecessary overhead, and complicates the entire test running process.

It’s a good thing then, that writing a custom karma reporter is fairly easy. I did it by forking a relatively simple reporter: karma-junit-reporter and making some adjustments, and I recommend you do the same.

However, i’ll make your life a little easier by explaining the basic structure, and what code segments you should adapt to your own needs.

Keep in mind that a Karma reporter is a Karma plugin.

Let’s create a “Hello World” reporter. This reporter will do the following things:

    1. write “Hello Word” when the execution starts
    2. write “Hello ” + browser name when starting to run tests on a browser
    3. write the test’s description on test completion
    4. write “yay” when a test passes
    5. write “nay” when a test fails
    6. write “GoodBye World” on execution end.
Introduction: Karma Plugins

Reporters are Karma plugins. Plugins are connected to Karma by a design pattern called Dependency Injection.

By default, Karma loads all NPM modules that are on the siblinks to it and their name matches karma-*. If you wish, you can create inline plugins, as explained in the documentation.

Our example reporter will be an independent NPM module.

Step 1: Create a “Karma-hello-reporter” project

You can create a new NPM module by using npm init.

Then you should npm link your project to a project with Karma tests. This way you see how your reporter works while you are working on it.

Now let’s look at the karma-junit-reporter on Github. It contains package.json, license and readme.md files.

You already have package.json, because npm init creates it automatically.

For this example project you don’t need a license and a readme file, since there is no need to publish it.

Everything you need for the reporter to work is inside index.js - unless of course you use outside dependencies.

Step 2: Add your reporter to Karma’s config file

In Karma’s config file (karma.conf.js by default), find the reporters property, and add ‘hello’ to the array.

If you are using the default settings, It should look like: reporters: ['progress', 'dots', 'hello'].

Add a new property to the config JSON:

helloReporter: {

successMsg: "yay",

failureMsg: "nay",

},

We’re done with the configurations. Let’s write some code. Open the index.js file.

Step 3: Define a constructor

var HelloReporter = function(baseReporterDecorator, config, logger, helper, formatError) {
};

The constructor takes as parameters the following functions (we’ll get to how these functions are passed to it later):

baseReporterDecorator: a function that takes an object and adds to it methods and properties of karma’s basic reporter (following the Decorator pattern).

config: The properties from Karma’s config.

logger: karma’s logger

helper: contains Karma utility functions, you may not need it

formatError: a function that takes an error object and returns a string with the error message and stack trace in a readable format.

If you wish to add your own log messages, add the line:

var log = logger.create('reporter.hello');

Add a variable for the configuration we added to Karma.conf.js

var helloConfig = config.helloReporter || {};

Let’s store our config data in local variables

var successMsg = helloConfig.successMsg';
var failureMsg = helloConfig.failureMsg';

We need to call ‘baseReporterDecorator’, because we wish to build on the basic reporter’s properties and methods.

baseReporterDecorator(this);

The reporter’s adapters are functions that generate the output.

Write:

this.adapters = [function(msg) {
process.stdout.write.bind(process.stdout)(msg
+ "rn");
}];

This means that you wish to send to the output to the console, and that every message you output will be on a different line.

Step 4: Listening to Karma’s Events

The Karma events loop consists of several events that you can write handlers to, thus adding desired functionality to the different stages of execution.

 

t-1

Browser properties: id, fullName, name, state, lastResult, disconnectsCount.

Browser.lastResult Properties: success(number), failed (number), skipped(number), total, totalTime, netTime, error(boolean), disconnected, totalTimeEnd

Browser.lastResult Methods: add

Browser methods: init, isReady, toString, onError, onInfo, onStart, onComplete, onDisconnect, reconnect, onResult, serialize, execute

BrowserCollection methods: add, remove, getById, setAllToExecuting, AreAllReady, serialize, getResults, clearResults, clone, map, forEach

Result properties: id, description,suite, success(boolean),

skipped, time, log

We don’t need to listen to all of these events.

Results properties: success(number), failed(number), error(boolean), disconnected(boolean), exitCode(number).

Write:

this.onRunStart = function(browsers) {
this.write("Hello World");
};

This pretty much speaks for itself. We don’t need to use browsers collection, but pay attention that you can also add or remove a browser on start, loop through them using forEach etc. as listed under browserCollection methods.

Next, we wish to greet the browsers when Karma establishes a connection with them.

Write:

this.onBrowserStart = function(browser) {
this.write("Hello " + browser.name);
};

When a single test is completed, we want to do two things; write the test description and yay/nay the result.

Write:

this.specSuccess = function(browser, result) {
this.write(helloConfig.successMsg);
}
this.specFailure = function(browser, result) {
this.write(helloConfig.failureMsg);
};

this.onSpecComplete = function(browser, result) {
if (result.skipped) {
this.specSkipped(browser, result);
}
else if (result.success) {
this.specSuccess(browser, result);
}
else {
this.specFailure(browser, result);
}
this.write(result.description);
}

Try removing this.onSpecComplete and see what happens. The nay/yay text is still written, and the description too, but in a different format. This is because the base reporter has a very similar this.onSpecComplete method.

Finally, the last item on our checklist, “Goodbye World”:

this.onRunComplete = function(browsersCollection, results) {
this.write("GoodBye World");
};

Step 5: Instruct Karma to call our constructor

As mentioned before, the plugins are connected to Karma using dependency injection. In order to pass our constructor Karma’s baseReporterDecorator, config etc. we inject them to it:

HelloReporter.$inject = ['baseReporterDecorator', 'config', 'logger', 'helper', 'formatError'];

The $inject property is used by the di npm module that is a dependency of Karma.

Finally, we export our constructor:

// PUBLISH DI MODULE
module.exports
= {
'reporter:hello': ['type', HelloReporter]
};

-“hello” is the name of the reporter we added earlier to the array of reporters in Karma’s configuration file.

-“type” means it’s a constructor. It instructs the di module’s injector to call new HelloReporter(...) to produce it.

-“HelloReporter” is the name we gave our constructor earlier.

Step 6: Running The Tests

That’s all, folks. Now you can run Karma and check your console to see the updated reports.

The next step is, of course modify the reporter the way you see fit and the sky's the limit.

May all your tests result with success… eventually.

Good luck!

Let's put these tips to good use

Grow your app business with ironSource