Injection error when using Jasmine with AngularJS - angularjs

I've been following the example of this developer for how to setup some basic Jasmine tests with AngularJS here
My problem seems to be that across my entire app, I am using one module which I refer to as myApp. However in my tests it complains about missing references.
app.js
(function () {
"use strict";
var myAppModule = angular.module('myApp', ['ngAnimate', 'ngRoute', 'ui.router', 'ngResource']);
})();
dogService,js
(function () {
"use strict";
var myAppModule = angular.module('myApp');
myAppModule.factory('Dog', function () {
var dogs = [
{ type: "Labrador", name: "Rover" },
{ type: "Tibetan Terrier", name: "Joey" },
{ type: "Yorkshire Terrier", name: "Rufus" }
];
return {
query: function () {
return dogs;
},
add: function (dog) {
dogs.push(dog);
}
};
});
}());
serviceSpec.js
///<reference path="~/Scripts/jasmine.js"/>
///<reference path="~/Scripts/jasmine-html.js"/>
///<reference path="~/Scripts/boot.js"/>
///<reference path="~/Scripts/angular.js"/>
///<reference path="~/Scripts/angular-mocks.js"/>
///<reference path="~/Scripts/App/app.js"/>
///<reference path="~/Scripts/App/Services/dogService.js"/>
"use strict";
describe("dogService", function () {
beforeEach(module("myApp"));
describe("Dog service", function () {
var dog;
beforeEach(inject(function ($injector) {
dog = $injector.get('Dog');
}));
it('should return 3 dogs when querying', function () {
expect(dog.query().length).toBe(3);
});
it('should return 4 dogs when querying after adding a dog', function () {
dog.add({ name: 'Fido', type: 'German Shepherd' });
expect(dog.query().length).toBe(4);
});
});
});
Finally my error:
finished in 0.071s2 specs, 2 failuresSpec List | FailuresSpec List | Failures
dogService Dog service should return 3 dogs when querying
Error: [$injector:modulerr] Failed to instantiate module myApp due to: Error: [$injector:modulerr] Failed to instantiate module ngAnimate due to: Error: [$injector:nomod] Module 'ngAnimate' is not available! You either misspelled the module name or forgot to load it.
If I change app.js to this, I won't get any errors, however I am going to have to inject dependencies at a top level in the module.
(function () {
"use strict";
var myAppModule = angular.module('myApp', []);
})();
Does anyone have any advice?

Was missing references of the angular files that are injected into myApp.
///<reference path="~/Scripts/angular-animate.js"/>
///<reference path="~/Scripts/angular-route.js"/>
///<reference path="~/Scripts/angular-ui-router.js"/>
///<reference path="~/Scripts/angular-resource.js"/>
Adding these fixed it but it's only a work-around solution because it's a lot of references to add for each test script.

Related

Angular test failing to instantiate module with Jasmine and Karma

I have setted up a test environment with Angular 1.6.6, ng-mock 1.6.6, Jasmine and Karma. But even with the easiest test I'm getting a [$injector:modulerr] when trying to inject the deloreanApp module
Failed to instantiate module deloreanApp due to: Error:
[$injector:nomod]
Theoretically there aren't tipo errors and Angular and ng-mock versions matches.
My files are:
app.js
(function () {
"use strict";
// initialize Angular
angular.module('deloreanApp', ['deloreanApp.controllers', 'deloreanApp.services']);
angular.module('deloreanApp.controllers', []);
angular.module('deloreanApp.services', []);
})();
controllers.js
(function () {
"use strict";
function deloreanController($scope){
$scope.sum = function(a,b){
return a + b;
}
}
angular.module('deloreanApp.controllers', []).controller('DeloreanController', ['$scope', deloreanController] );
})();
DeloreanController.test.js
describe('calculator', function () {
beforeEach(module('deloreanApp'));
var $controller;
beforeEach(inject(function (_$controller_) {
$controller = _$controller_;
}));
describe('sum', function () {
it('1 + 2 should equal 3', function () {
var result = 3;
expect(result).toBe(3);
});
});
});
And part of my karma.conf.js file:
// list of files / patterns to load in the browser
files: [
'lib/angular.min.js',
'lib/angular-mocks.js',
'js/app.js',
'js/controllers.js',
'tests/DeloreanController.test.js'
],
Try this,
(function () {
"use strict";
angular.module('deloreanApp.controllers', []);
angular.module('deloreanApp.services', []);
// initialize Angular
angular.module('deloreanApp', ['deloreanApp.controllers', 'deloreanApp.services']);
})();

Angular app test with Karma

I'm new to Jasmine tests with Karma. I'm getting following error while executing the tests.
Here are my files.
message.service.js // Service file
(function() {
'use strict';
angular.module("app").factory('MessageService', [MessageService]);
function MessageService() {
var service = {};
//var gui = require('nw.gui');
service.alert = function() {
//gui.Window.open('/');
//$window.open('/message');
return 'hello';
};
return service;
};
})(); // IIFE
message.service.spec.js // Service test file
describe('Message Service', function() {
var messageService;
beforeEach(angular.mock.module('app'));
beforeEach(inject(function(_MessageService_) {
messageService = _MessageService_;
}));
it('should exist', function() {
expect(messageService).toBeDefined();
});
it('should return hello', function() {
expect(messageService.alert()).toEqual('hello');
});
});
app.module.js // Main app file
(function() {
'use strict';
angular.module("app", ['ngRoute', 'ui.bootstrap', 'ui.router']);
/*angular.module("app").run(['$rootScope', 'StartupService', function($rootScope, startupService){
startupService.init($rootScope);
}]);*/
})(); // IIFE
If I change following line in message.service.js
angular.module("app").factory('MessageService', [MessageService]);
to
angular.module("app",[]).factory('MessageService', [MessageService]);
Then tests work fine but application will not work(due to re initiation of module I guess). How can I make my app to work for both tests and normal app execution? Appreciate your help
It was my mistake. I haven't included the js file in Karma config related to the ui.bootstrap module. After that it worked fine.
For anyone who gets such error check whether you have included all the dependency files on Karma config, what you have defined in main app.
ex: assume your main app file is like below.
angular.module("app", ['ngRoute', 'ui.bootstrap', 'ui.router']);
Then your Karma config file should look like this
files: [
'assets/lib/angular/angular.min.js',
'assets/lib/angular-ui-router/angular-ui-router.min.js',
'assets/lib/angular-bootstrap/ui-bootstrap-tpls-1.3.2.min.js'
],
Thanks for your help.

How to access angular app module that is defined in self invoke?

I want use unit test in angular, but the webapp that I working on it is created on its owe structure. for example :
(function() {
'use strict';
angular
.module('app', [
'ngAnimate',
'ui.load',
'ui.jp',
'oc.lazyLoad'
]);})();
And one of its controllers :
(function() {
'use strict';
angular
.module('app')
.controller('EditorCtrl', EditorCtrl);
function EditorCtrl($scope) {
var vm = $scope;
vm.options = {
toolbar: [
['style', ['bold', 'italic', 'underline', 'clear']],
]
};
}})();
And I have no idea how to use unit test to this app because my test cant find controller.
This is my controller and test :
(function () {
'use strict';
angular
.module('app')
.controller('DashboardCtrl', dashboard);
describe('test dashboard', function () {
beforeEach(module('DashboardCtrl'));
var $controller;
beforeEach(inject(function (_$controller_) {
$controller = _$controller_;
}));
describe('sum', function () {
it('1 + 1 should equal 2', function () {
var $scope = {};
var controller = $controller('DashboardCtrl', {$scope: $scope});
$scope.x = 1;
$scope.y = 2;
$scope.sum();
expect($scope.z).toBe(3);
});
it('z should default to zero', function () {
var $scope = {};
var controller = $controller('DashboardCtrl', {$scope: $scope});
expect($scope.z).toBe(0);
});
});
});
function dashboard($scope) {
$scope.name = 'Dashboard';
$scope.z = 0;
$scope.sum = function () {
$scope.z = $scope.x + $scope.y;
};
}
})();
And in karma test show me this error :
Error: [$injector:modulerr] Failed to instantiate module DashboardCtrl due to:
Error: [$injector:nomod] Module 'DashboardCtrl' 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.
That indicate that cant find "DashboardCtrl" controller.
the problem solved by add all modules in files options of karma.conf.js
the point is event lake of one of modules that are injected is main module that here is app stop your test, so addition to include your controller you need to add all module are mentioned.
When unit testing use $controller for getting a handle on a controller instance https://docs.angularjs.org/api/ng/service/$controller like var ctrlToTest = $controller('EditorCtrl')(newTestScope); To create a new test scope you can use var newTestScope = $rootScope.$new()

Angular unit test works in browser but errors in Chutzpah

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": "./"
}

Angular testing [$injector:unpr] Unknown provider: STARTUP_CONFIG

I'm having trouble getting my tests to run due to dependencies not beeing injected correctly.
The error I'm getting is defined in the title. I've included the actual test code, the app.js & index.html file from my solution.
The problem lies with the deferred bootstrap which I'm not fimiliar with as it was included by one of my colleagues. If I remove the "app.config(function (STARTUP_CONFIG..." from the app.js file then the test runs fine
How can I correctly inject the STARTUP_CONFIG in my test?
test code
..
..
describe("test description...", function () {
var app;
var mockupDataFactory;
beforeEach(module('Konstrukt'));
beforeEach(inject(function (STARTUP_CONFIG,BUDGETS,APPLICATIONS) {
//failed attempt to inject STARTUP_CONFIG
}));
beforeEach(function () {
app = angular.module("Konstrukt");
});
beforeEach(function () {
mockupDataFactory = konstruktMockupData.getInstance();
});
it('should be accessible in app module', function () {
expect(app.pivotTableService).toNotBe(null); //this test runs fine
});
it('test decr...', inject(function ( pivotTableService) {
... //Fails here
..
..
app.js
..
..
angular.module('Konstrukt', ['ngGrid', 'ngSanitize', 'ngRoute','pasvaz.bindonce', 'ngAnimate', 'nvd3ChartDirectives', 'ui.select', 'ngProgress', 'ui.grid', 'ui.grid.edit','ui.grid.selection', 'ui.grid.cellNav', 'ui.grid.pinning', 'ui.grid.resizeColumns']);
var app = angular.module('Konstrukt');
app.config(function (STARTUP_CONFIG, BUDGETS, APPLICATIONS) {
var STARTUP_CONFIG = STARTUP_CONFIG;
var BUDGETS = BUDGETS;
var APPLICATIONS = APPLICATIONS;
});
..
..
index.html
..
..
<script>
setTimeout(function(){
window.deferredBootstrapper.bootstrap({
element: window.document.body,
module: 'Konstrukt',
resolve: {
STARTUP_CONFIG: ['$http', function ($http) {
return $http.get('/scripts/_JSON/activeBudgets.JSON');
}],
BUDGETS: ['$http', function ($http) {
return $http.get('/scripts/_JSON/activeBudgets.JSON');
}],
APPLICATIONS: ['$http', function ($http) {
return $http.get('/scripts/_JSON/applications.JSON');
}]
}
})
} , 1500);
</script>
The deferredBootstrapper will not run in your unit tests, which means the constants it normally adds to your module won't be available.
You can add a global beforeEach that provides mocked versions of them:
beforeEach(function () {
module(function ($provide) {
$provide.constant('STARTUP_CONFIG', { something: 'something' });
$provide.constant('BUDGETS', { something: 'something' });
$provide.constant('APPLICATIONS', { something: 'something' });
});
});

Resources