Skip to content

Testing: Unit tests

Jonathan Niles edited this page Oct 2, 2018 · 7 revisions

Unit Testing

Unit tests are central to the 2.x focus on small modular components that interact with each other in a testable, maintainable way. Unit tests allow us to test each individual interaction (component) making sure that as we make changes to the application the behavior is still as expected.

NOTE: Travis is now configured to run integration tests automatically.

Benefits

  1. Fast - Unit tests run independent of the overall application, as they do not require the environment set-up/ destruction they can be run rapidly and in real time.
  2. Wide code coverage - Unit tests can be written to cover controllers, components, directives, services, factories, filters; just about any Angular module can be unit tested.
  3. Focused - Unit tests are designed to test very small and focused functionality, this provides confidence when stringing together many different modules. It also allows end to end tests to focus more on abstract concepts.

Running Unit Tests

Unit tests are run using the karma test runner, they can be run using the command line interface.

npm install -g karma-cli

karma start # This will start the karma runner and open a Google Chrome instance to run the tests
karma run # Run the unit tests on the currently open karma runner

In the future unit tests will also be integrated into the build process.

Writing Unit Tests

We currently focus on testing Angular services and directives; controller unit tests will be used increasingly in the future however for now they are not required.

  • Angular services
    • Does the angular service respond to errors appropriately?
    • Do the utility methods exposed from the service provide expected output given known input?
    • Does the service work in context (e.g filter provide the correct values given html input)
  • Angular directives
    • Does the directive correctly update form validity?
    • Does the directive display the correct values given a certain input?
    • Are constraints respected as appropriate?
    • Does the directive correctly display errors and update the user?

Unit tests use the mocha assertion library however the karma runner has already configured the environment by the time the .spec.js test is run - this means you can assume everything is imported and ready to use.

describe('Specific input validation directive', function () { 
  // everything exists here (before, beforeEach, expect, etc.)

});

The beforeEach method will be run before every unit test - we can use this to ensure the test has the correct Angular modules available to run the tests. module() - Provide an Angular module into the current test scope inject() - Provide Angular dependency injection into the current test scope

// expose the directives module - this will include the Specific input validation directive we aim to test
beforeEach(module('bhima.directives'));
var $scope;

beforeEach(inject(function ($rootScope) { 
  // $rootScope is now available to the entire test block
  $scope = $rootScope;
});

Note: Custom BHIMA services cannot be injected using the Angular literal dependency injection (for example how we imported the default Angular service $rootScope) - however we can use _UnderscoreSyntax_ to ensure Angular knows how to import the service.

// inject the custom BHIMA SessionService (remember unless it is a default Angular service `_` must be used
var Session;

beforeEach(inject(function (_SessionService_) { 
  Session = _SessionService_;
});

From here anything that is possible in Angular can be performed in a unit test - for example we can use the $compile service to actually test what will happen when angular calls $digest on a directive given specific input.

var directive = angular.element('<form name="form"><input name="val" specific-validation></form>');
$compile(directive)($scope)
$scope.form.val.$setViewValue(...

This documentation will be expanded as unit tests are integrated into the standard code review/ development workflow.

Module dependencies 'gotchas'

$injector Error thrown

It is important to note that any dependency of the injected Service, as well as their dependencies, will have to be included in the beforeEach module import, otherwise the test will fail on citing an $injector error. For example:

// ensure all required modules are available
// it is important to note that `SessionService` relies on `angularMoment` and `ngStorage`
// if this is not imported and made available to this test file, an `$injector` error 
// will be thrown
var Session;

beforeEach(module('bhima.services', 'angualarMoment', 'ngStorage'));

beforeEach(inject((_SessionService_) => { 
  Session = _SessionService_;
}));

Template 'gotchas'

If you get an error such as the following:

	Error: Unexpected request: GET /modules/templates/bhFilterToggle.tmpl.html

This may be because you used an absolute path in your controller's template field. Karma cannot resolve the absolute path, instead it needs a relative path. Change:

{
  template : '/modules/templates/bhFilterToggle.tmpl.html',
  // etc...
}

to

{
  template: 'modules/templates/bhFilterToggle.tmpl.html', // look ma, no beginning slash!
  // etc ...
}