Getting started with Karma for AngularJS Testing
A quick-start guide to writing unit tests for AngularJS controllers using Karma and Jasmine.
on
This post follows on from Testing your First AngularJS Controller which demonstrated how to unit test an AngularJS controller using Jasmine. We concluded the previous post by mentioning a better workflow. The workflow of that process was to:
- Make code changes
- Refresh browser window of containing page
- See test results
The main drawback to this approach is that we had to keep switching back to the browser and refreshing it once we had made code changes. What if we wanted to test against multiple browsers? We could end up having to constantly refresh multiple browser windows after each code change to keep up. We can make use of a test runner to automate this process.
Introducing Karma
A tool called Karma is a JavaScript test runner created by the AngularJS team. Jasmine is the testing framework that we talked about in the getting started with unit testing for AngularJS post, and Karma provides helpful tools that make it easier to us to call our Jasmine tests whilst we are writing code.
We’re going to rebuild the single page calculator app from the last post using Karma for the tests.
Installing Karma
You can read the full installation instructions here or we also summarise them below. The first step is to install Node.js:
- Install Node.js
NB the documentation states that: Karma works on the two latest stable versions. That is 0.8.x and 0.10.x at this point. However, I have Node.js v0.12.x and have yet to run into any problems.
Node: For the following commands I assume use of Terminal for Mac or Command Prompt for Windows.
Open the Terminal/Command Prompt and enter the following:
mkdir CalculatorKarma
cd CalculatorKarma
echo {} >> package.json
Next we need to install the Karma tool via the NPM package manager:
npm install karma --save-dev
What’s –save-dev? That’s an option that updates the installed packages in the ‘devDependencies’ section of the package.json file. It signifies that a developer will need this package to work with the application, but it’s not required to run the applicaiton i.e. in production.
If all went well, the package.json file should have the following contents:
{
"devDependencies": {
"karma": "^0.12.31"
}
}
It’s useful to install the karma-cli (especially if you are on Windows and want to use the Command Prompt) since it makes the command karma globally available:
npm install -g karma-cli
Note: if you would rather not install this globally, where we see the use of karma in the remaining commands for this post, use ./node_modules/karma/bin/karma instead.
The next step is to install karma plug-ins to enable us to use the Jasmine test framework and Google Chrome as the target browser:
npm install karma-jasmine karma-chrome-launcher --save-dev
The package.json contents should now have the following:
{
"devDependencies": {
"jasmine-core": "^2.3.4",
"karma": "^0.12.31",
"karma-chrome-launcher": "^0.1.12",
"karma-jasmine": "^0.3.5"
}
}
Writing Tests
Next we will create a test. In the Terminal/Command prompt enter:
mkdir tests
touch tests/calculator.controller.test.js
Paste the following test code into this new file (these are the tests from the previous post):
describe('calculator', function () {
beforeEach(module('calculatorApp'));
var $controller;
beforeEach(inject(function(_$controller_){
$controller = _$controller_;
}));
describe('sum', function () {
it('1 + 1 should equal 2', function () {
var $scope = {};
var controller = $controller('CalculatorController', { $scope: $scope });
$scope.x = 1;
$scope.y = 2;
$scope.sum();
expect($scope.z).toBe(3);
});
});
});
Configuring the Test Runner
Before we can run this test we need to create a configuration file for the karma settings. You can read the official configuration documentation or follow the summarised instructions below.
To create this file via a step by step wizard that comes with karma, enter the following in the Terminal/Command prompt:
karma init karma.conf.js
Answer the prompts as follows:
- Which testing framework do you want to use?
Hit return to accept the default value i.e. jasmine.
- Do you want to use Require.js ?
Hit return to accept the default value i.e. no.
- Do you want to capture any browsers automatically ?
Hit return to accept the default value i.e. Chrome.
- What is the location of your source and test files ?
Enter the following value:
tests/*.test.js
Don’t worry if you accidentally skipped this, we can directly edit the config file at a later stage.
- Should any of the files included by the previous patterns be excluded ?
Hit return to accept the default value.
- Do you want Karma to watch all the files and run the tests on change ?
Hit return to accept the default value. i.e. yes
The config file called karma.conf.js will be created in the root folder.
We can use this file to run the tests from the Terminal/Command Prompt with the command:
karma start karma.conf.js
The test output should look as follows, since we have only a test with no implementation.
> @ test /Users/devuser/repos/CalculatorKarma
> ./node_modules/karma/bin/karma start karma.conf.js
INFO [karma]: Karma v0.12.31 server started at http://localhost:9876/
INFO [launcher]: Starting browser Chrome
INFO [Chrome 42.0.2311 (Mac OS X 10.10.3)]: Connected on socket 2absOkNfa1asasaX0fCJ with id 71338229
Chrome 42.0.2311 (Mac OS X 10.10.3) calculator encountered a declaration exception FAILED
ReferenceError: module is not defined
at Suite.<anonymous> (/Users/devuser/repos/CalculatorKarma/tests/calculator.controller.test.js:3:13)
at /Users/devuser/repos/CalculatorKarma/tests/calculator.controller.test.js:1:1
Chrome 42.0.2311 (Mac OS X 10.10.3): Executed 1 of 1 (1 FAILED) ERROR (0.01 secs / 0.005 secs)
Enter Ctrl + C in the Terminal/Command Prompt to terminate the process.
Note: as a slight optimisation I like to update the package.json with the scripts section as follows:
{
"scripts": {
"test": "karma start karma.conf.js"
},
"devDependencies": {
"jasmine-core": "^2.3.4",
"karma": "^0.12.31",
"karma-chrome-launcher": "^0.1.12",
"karma-jasmine": "^0.3.5"
}
}
This change means that I can enter npm test rather than the specific karma configuration detail.
To verify the change to package.json, running the following from the Terminal/Command prompt will also start the karma process:
npm test
Making the Tests Pass and Adding Functionality
Next we will add the controller logic for the test. In the Terminal/Command prompt enter:
mkdir app
touch app/calculator.controller.js
We also need to download the angular and angular mock libraries that we used in the previous post. Run the following:
mkdir lib
curl -o lib/angular.min.js https://code.angularjs.org/1.4.0-rc.2/angular.min.js
curl -o lib/angular-mocks.js https://code.angularjs.org/1.4.0-rc.2/angular-mocks.js
We need to tell the karma configuration about these new folders/files, in order to do this edit the karma.conf.js file that was created by the wizard and update the files section to be as follows:
Replace this section:
// list of files / patterns to load in the browser
files: [
'tests/*.test.js'
],
With this section:
// list of files / patterns to load in the browser
files: [
'lib/angular.min.js',
'lib/angular-mocks.js',
'app/*.js',
'tests/*.js'
],
Start the test runner again via the command:
npm test
We should still see a failing test since we’ve yet to add the controller code. Let’s do that next. Leave the Karma process running and paste the following code into the controller file we added at app/calculator.controller.js:
angular.module('calculatorApp', []).controller('CalculatorController', function CalculatorController($scope) {
$scope.sum = function() {
$scope.z = $scope.x + $scope.y;
};
});
The Terminal/Command Prompt output should have detected the change to the file, re-executed the tests and we should now see a passing test.
With karma still running, let’s add a second test. Paste the following code into the test file calculator.controller.test.js. Add it within the same ‘describe’ block as the existing test:
it('z should have default value of zero', function () {
var $scope = {};
var controller = $controller('CalculatorController', { $scope: $scope });
expect($scope.z).toBe(0);
});
For reference, your test file should look like this.
The console output will report a failing test. I will leave it to you to update the controller to make this test pass (or you can see the change required here).
If all went to plan, the output console should show something along the lines of:
More About Karma
I’ve written about Karma before, but without integrating AngularJS.
A key feature is being able to run tests against multiple browsers at the same time, in particular:
There are a lot of plug-ins for Karma available via NPM.
Source Code
The full code for this example is available as a Github project.