Unit testing helps maintain clean code, as such I included some of my recommendations for unit testing foundations with links for more information.
Write a set of tests for every story. Start with an empty test and fill them in as you write the code for the story.
Why?: Writing the test descriptions helps clearly define what your story will do, will not do, and how you can measure success.
it('should have Avengers controller', function() {
// TODO
});
it('should find 1 Avenger when filtered by name', function() {
// TODO
});
it('should have 10 Avengers', function() {
// TODO (mock data?)
});
it('should return Avengers via XHR', function() {
// TODO ($httpBackend?)
});
// and so on
Use Jasmine or Mocha for unit testing.
Why?: Both Jasmine and Mocha are widely used in the Angular community. Both are stable, well maintained, and provide robust testing features.
Note: When using Mocha, also consider choosing an assert library such as Chai. I prefer Mocha.
Use Karma as a test runner.
Why?: Karma is easy to configure to run once or automatically when you change your code.
Why?: Karma hooks into your Continuous Integration process easily on its own or through Grunt or Gulp.
Why?: Some IDE’s are beginning to integrate with Karma, such as WebStorm and Visual Studio.
Why?: Karma works well with task automation leaders such as Grunt (with grunt-karma) and Gulp. When using Gulp, use Karma directly and not with a plugin as the API can be called directly.
/* recommended */
// Gulp example with Karma directly
function startTests(singleRun, done) {
var child;
var excludeFiles = [];
var fork = require('child_process').fork;
var Server = require('karma').Server;
var serverSpecs = config.serverIntegrationSpecs;
if (args.startServers) {
log('Starting servers');
var savedEnv = process.env;
savedEnv.NODE_ENV = 'dev';
savedEnv.PORT = 8888;
child = fork(config.nodeServer);
} else {
if (serverSpecs && serverSpecs.length) {
excludeFiles = serverSpecs;
}
}
var karmaOptions = {
configFile: __dirname + '/karma.conf.js',
exclude: excludeFiles,
singleRun: !!singleRun
};
let server = new Server(karmaOptions, karmaCompleted);
server.start();
////////////////
function karmaCompleted(karmaResult) {
log('Karma completed');
if (child) {
log('shutting down the child process');
child.kill();
}
if (karmaResult === 1) {
done('karma: tests failed with code ' + karmaResult);
} else {
done();
}
}
}
Use Sinon for stubbing and spying.
Why?: Sinon works well with both Jasmine and Mocha and extends the stubbing and spying features they offer.
Why?: Sinon makes it easier to toggle between Jasmine and Mocha, if you want to try both.
Why?: Sinon has descriptive messages when tests fail the assertions.
Use PhantomJS to run your tests on a server.
Why?: PhantomJS is a headless browser that helps run your tests without needing a “visual” browser. So you do not have to install Chrome, Safari, IE, or other browsers on your server.
Note: You should still test on all browsers in your environment, as appropriate for your target audience.
Run JSHint on your tests.
Why?: Tests are code. JSHint can help identify code quality issues that may cause the test to work improperly.
Relax the rules on your test code to allow for common globals such as describe
and expect
. Relax the rules for expressions, as Mocha uses these.
Why?: Your tests are code and require the same attention and code quality rules as all of your production code. However, global variables used by the testing framework, for example, can be relaxed by including this in your test specs.
/* jshint -W117, -W030 */
Or you can add the following to your JSHint Options file.
"jasmine": true,
"mocha": true,
Place unit test files (specs) side-by-side with your client code. Place specs that cover server integration or test multiple components in a separate tests
folder.
Why?: Unit tests have a direct correlation to a specific component and file in source code.
Why?: It is easier to keep them up to date since they are always in sight. When coding whether you do TDD or test during development or test after development, the specs are side-by-side and never out of sight nor mind, and thus more likely to be maintained which also helps maintain code coverage.
Why?: When you update source code it is easier to go update the tests at the same time.
Why?: Placing them side-by-side makes it easy to find them and easy to move them with the source code if you move the source.
Why?: Having the spec nearby makes it easier for the source code reader to learn how the component is supposed to be used and to discover its known limitations.
Why?: Separating specs so they are not in a distributed build is easy with grunt or gulp.
/src/client/app/customers/customer-detail.controller.js
/customer-detail.controller.spec.js
/customers.controller.js
/customers.controller.spec.js
/customers.module.js
/customers.route.js
/customers.route.spec.js