Karma test runner is a really simple and relatively easy way to run JavaScript across multiple browsers, automatically test code changes (using watchers) and it also integrates neatly into task runners like Grunt, so it can be chained nicely into a Lint > Build > Test > Distribute
type of process. Coupled with this, it really plays well with RequireJS…
The only problem is, when things go wrong with misconfiguration you can run into errors like :
There is no timestamp for /base/src/someScript.js
or
Mismatched anonymous define() module: ...
So the first time you’re setting it up, it helps to understand those errors and how to patch them up and also to know the sequence of configs and package installations to get off the ground. If you’re just interested in those errors see Troubleshooting.
In this lab I’ll walk through (in fairly painful detail!) setting up a web app and all the scaffolding required. Each step has been committed to a git repository for reference, so after initial setup you can advance through the lab manually (recommended!) or use git to checkout each step. view steps on github
Step 0 - Prerequisites and Initial Setup
This lab assumes you have npm
and git
installed and that you are familiar with both.
To get started with the lab, clone the repository and checkout step 0 - basic structure
as follows:
In your dev folder, run the following
git clone https://github.com/stephen-james/lab-karma-require-jasmine.git
Change directory to the cloned repo
cd lab-karma-require-jasmine
And checkout the starting point of this lab, step 0…
git checkout step0
While everyone has their own preferred folder structure for web apps, for the purposes of this lab we’ll be using the following basic structure :
|
|
Step 1 - Initialising the Web Application as an npm package
Its typical in a web app that we’ll have quite a few dependencies, but we don’t want to commit these to our repository. Its way cleaner to use a package manager that stores a list of dependencies and to exclude them explicitly using a .gitignore
file.
For this lab, we’ll be using npm as the package manager. So we should create a .gitignore
file to ignore any dependencies it loads in node_modules
Next up we’ll create the package.json
file for this project using npm. While we’d never want to actually publish this sample app on npm, this will help us describe the project and in future steps will also list dependencies. more information on package.json
You can follow along through the npm init wizard, or alternatively just create the package.json manually.
Using npm init
Run npm init
from the root folder of the web app, follow the sample below…
Sample : using npm init from comand line
|
|
package.json
Alternatively manually create package.json as follows…
Step 2 - Setting up RequireJS and the browser default page
To keep our JavaScript nice and modular and manageable we’re going to use RequireJS.
Install RequireJS as a dependency of the web app and save the dependency meta info in the package.json
file.
npm install requirejs --save
Now in the root folder of the web app, we’re going to edit index.html
to include a script reference to RequireJS.
Add the following script tag at the bottom of the <body>
<script src="node_modules/requirejs/require.js" data-main="src/main.js"></script>
Our index.html
now looks like this
We’re referencing RequireJS in the location that npm has installed it for us and telling Require that it should bootstrap the app by running src/main.js
If we were to run the web app now, we’d get a 404 because src/main.js
doesn’t exist yet and RequireJS would throw a Script error.
We need to set up our RequireJS bootstrap…
Step 3 - Setting up the RequireJS bootstrap and App entry point
In the /src
folder we’re going to create two files, main.js
and app.js
. main.js
will contain the JavaScript module configuration for RequireJS and app.js
will be the real entry point to the web app, which will be fired up once RequireJS has performed it’s magic.
Install jQuery, it’ll serve as an example dependency for our app
npm install jquery --save
Create src/app.js
and src/main.js
In main.js
, we’re configuring RequireJS, telling it where to find jQuery and that once it’s configured the modules it should launch our app, by calling app.start()
If we point a browser to index.html
we should now see the simple message that the app has started up, coming from app.js
this.target.html("App Started!");
Step 4 - Getting some Karma!
Now that the initial strawman is there for our web app, lets get that test runner going and start putting in some test specs!
We need to install karma as a development dependency of the application
npm install karma --save-dev
To run the karma client from the command line, we must install it globally
npm install karma-cli -g
Karma requires a karma.conf.js
configuration file, which we can write to a file manually or create using the console ‘wizard’ by running karma init
.
Using the Karma init console wizard
Run the following from the command line
karma init
selecting the following values :
|
|
Ignore the warnings about non-matching files, provided you got the paths right that just means that those paths don’t contain any test specs yet.
Karma has now created a config file which describes how we want to Karma to perform karma.conf.js
and an entry point for RequireJS (for Karma, not our app) test-main.js
. To keep things clean, move the RequireJS entry point/bootstrap file to the test folder so that our structure now looks like this :
|
|
Edit karma.conf.js
to look for test-main.js
in the test folder.
|
|
Edit test/test-main.js
to look like the following
We’re changing a couple of values from the boilerplate template, setting the baseUrl
to be /base/src
so that dependency definitions are consistent between main.js
and test-main.js
and adding paths for jQuery and ensuring that the test folder has a relative mapping.
Start Karma up from the command line
karma start
Because we chose to have Chrome and PhantomJS as browsers for testing, the testrunner will show feedback in both the command line and a launched Chrome instance.
Step 5 - Creating a Spec
Create a subfolder test/app
and create spec file startup.spec.js
.
Karma’s watcher will pick this spec up automatically and execute it
Troubleshooting
Two errors you can get pretty easily through misconfiguration are :
There is no timestamp for /base/src/someScript.js
Mismatched anonymous define() module: ...
No Timestamp Errors
This indicates that Karma doesn’t have that file in it’s file list. All files need to be included in this list, the app files, library files / dependencies and test specs too. If a file is missing from this list, Karma will put up a warning for that particular file.
So in our sample app, if we modified our filelist in karma.conf.js
from :
|
|
to this (removing the jquery reference):
|
|
Karma’s web server won’t serve the file and we’ll get error output similar to :
|
|
Mismatched anonymous define() module
Again this is an issue with karma.confg.js
, when we’re using RequireJS with Karma we must make sure that any files that will be required as dependencies from the test specs are included in the file list with the included: false
option. This will make sure that the script is not loaded twice (which causes the mismatch).
|
|
If you want to see this in action, we can simulate this problem by switching included:true
on src/*.js
using the lab files for an example.
Another thing to ensure is that the Application’s RequireJS bootstrap main.js
is in the exclude setting.
|
|
Using Jasmine 2.0 with Karma
At the time of writing this, Karma installed Jasmine 1.3.1 by default, if you want to use version 2.0 of Jasmine, make sure you specify the Karma version to be 0.2.0 or higher… more info on Karma with Jasmine 2.0 here