Configuring Karma for Bower Dependencies
Although, I have now configured my Cordova project to automatically copy distribution files from installed Bower packages and reference them from HTML pages, there's still one final part of Bower-related configuration left: unit tests also require Bower dependencies. I don't want to manually update the configuration of my test runner every time I install an additional Bower package.
Since I already described basic Karma configuration for a TypeScript project in a previous blog post, I'm going to take that as my starting point and focus on automatically referencing the Bower packages. As expected, there's a plugin available for that: wiredep. At least for me, its name made it more difficult to find and identify as the right solution for my problem. However, it turned out really easy to use:
Install the plugin:
npm install --save-dev karma-wiredep
Add it as a framework to
karma.conf.js
:frameworks: ["wiredep", "jasmine"]
Add its own configuration block
karma.comf.js
:wiredep: { dependencies: true, devDependencies: true }
There are some additional configuration options available, but at least for now, this works for me.
As you can see, I chose to include both production and development dependencies. The reason for that is Angular, which I am using in my project. To unit test AngularJS code effectively, angular-mocks is invaluable. In my very first test, I already used its module
loader and mocked $httpBackend
.
Of course, you don't want to have the mocks included in your application, therefore the package needs to be installed as a development dependency:
bower install angular-mocks --save-dev
Because I'm still in the project setup phase, there's not a lot of application code to test. To try out the configuration, I decided to write a couple of tests for the AngularJS routing in its current state:
class Routes {
static $inject = ["$locationProvider", "$routeProvider"];
constructor($locationProvider: ng.ILocationProvider, $routeProvider: ng.route.IRouteProvider) {
$locationProvider.hashPrefix("!");
$routeProvider
.when("/title",
{
templateUrl: "views/title.html"
})
.when("/game",
{
templateUrl: "views/game.html"
})
.otherwise({
redirectTo: "/title"
});
}
}
angular.module("app")
.config(Routes);
To be honest, the tests are rather trivial. I'm just checking that the right template is being used for each path:
describe("app routes", () => {
var $route: ng.route.IRouteService;
var $httpBackend: ng.IHttpBackendService;
var $location: ng.ILocationService;
var $rootScope: ng.IRootScopeService;
beforeEach(angular.mock.module("app"));
beforeEach(() => {
inject((_$route_: ng.route.IRouteService, _$httpBackend_: ng.IHttpBackendService, _$location_: ng.ILocationService, _$rootScope_: ng.IRootScopeService) => {
$route = _$route_;
$httpBackend = _$httpBackend_;
$location = _$location_;
$rootScope = _$rootScope_;
});
});
beforeEach(() => {
expect($route.current).toBeUndefined();
$httpBackend.expectGET(/.*/).respond(200);
});
it("route title to title.html", () => {
$location.path("/title");
$rootScope.$digest();
expect($route.current.templateUrl).toBe("views/title.html");
});
it("route game to game.html", () => {
$location.path("/game");
$rootScope.$digest();
expect($route.current.templateUrl).toBe("views/game.html");
});
it("route unknown paths to title.html", () => {
$location.path("/unknown");
$rootScope.$digest();
expect($route.current.templateUrl).toBe("views/title.html");
});
});
As simple as these tests are, they are enough to prove that Karma is correctly configured. All dependencies were loaded successfully, otherwise the tests would fail. Any dependencies that I might add later, will automatically be loaded as well, minimizing the overhead of writing unit tests, which is always a good thing.