I have an angular module which uses the angular ui bootstrap typeahead directive. I am attempting to unit test this with qunit and chutzpah.
The unit test runs and passes when run through a browser, but returns an error when run with Chutzpah:
Error: Error: [$injector:modulerr] Failed to instantiate module myApp due to: Error: [$injector:nomod] Module 'myApp' is not available! You either misspelled the module name or forgot to load it. If registering a module ensure that you specify the dependencies as the second argument.
Why is this error occurring?
JS
(function () {
var app = angular.module('myApp', ['ui.bootstrap']);
app.factory('myFactory', ['$http', function ($http) {
var myFactory = {};
myFactory.getLocations = function (query) {
return $http.get('/Locations', { params: { query: query } });
};
return myFactory;
}]);
app.controller('MyController', ['myFactory', function (myFactory) {
this.location = '';
this.getLocations = function (query) {
return myFactory.getLocations(query).then(function (response) {
return response.data;
});
};
}]);
})();
HTML
<div data-ng-app="myApp" data-ng-controller="MyController as my">
<input name="location" type="text" data-ng-model="my.location" data-typeahead="location for location in my.getLocations($viewValue)" class="form-control">
</div>
Unit Test
///<reference path="angular.js"/>
///<reference path="angular-mocks.js"/>
///<reference path="angular-ui/ui-bootstrap-tpls.js"/>
///<reference path="qunit-1.15.0.js"/>
///<reference path="sinon-1.9.1.js"/>
///<reference path="myapp.js"/>
var appMocks = angular.module("appMocks", []);
appMocks.config(function ($provide) {
$provide.decorator('$httpBackend', angular.mock.e2e.$httpBackendDecorator);
});
var injector = angular.injector(['ng', 'myApp', 'appMocks']);
var scope = {};
var $controllers = injector.get('$controller');
var $httpBackend = injector.get('$httpBackend');
var myFactory = injector.get('myFactory');
QUnit.module("MyApp Tests", {
setup: function () {
scope = injector.get('$rootScope').$new();
$controllers('MyController as my', {
$scope: scope,
myFactory: myFactory
});
}
});
QUnit.test('Get locations returns valid locations', function (assert) {
$httpBackend.expectGET('/Locations?query=l').respond(['london', 'leeds']);
var result;
scope.my.getLocations('l').then(function (response) {
result = response;
});
$httpBackend.flush();
assert.equal(2, result.length, "correct number of results returned");
});
Chutzpah Settings
{
"Framework": "qunit",
"CodeCoverageIncludes": ["*.js"],
"CodeCoverageExcludes": [
"*\\sinon-1.9.1.js",
"*\\angular.js",
"*\\angular-mocks.js",
"*\\angular-ui/ui-bootstrap-tpls.js",
"*\\Tests\\*"
],
"RootReferencePathMode":"SettingsFileDirectory",
"TestHarnessDirectory": "./"
}
The issue is one of .js loading order. When you are running with code coverage the blanket.js plugin will instrument your files and cause them to load asynchronously. Since you were excluding your test files from instrumentation it was loading it before the source file. To change this just allow your test file to be instrumented by removing "\test-js\" from your coverageexcludes section:
{
"Framework": "qunit",
"CodeCoverageIncludes": ["*.js"],
"CodeCoverageExcludes": [
"*\\angular/angular.js",
"*\\angular/angular-mocks.js",
"*\\angular-ui/ui-bootstrap-tpls.js",
"*\\qunit/qunit-1.15.0.js",
"*\\sinon/sinon-1.9.1.js"
],
"RootReferencePathMode":"SettingsFileDirectory",
"TestHarnessDirectory": "./"
}