Angular test failing to instantiate module with Jasmine and Karma - angularjs

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']);
})();

Related

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.

Angular Unit Testing Factory Injection

I've seen this answered but none of the solutions are working for me. I'm getting an error Error: [ng:areq] Argument 'fn' is not a function, got undefined which is causing my test to fail. What's the correct way to inject a factory into a test spec that isn't a stubbed factory.
login module
angular.module('loginModule', ['ui.bootstrap', 'permission', 'ui.router', 'coreModule', 'utilsModule']);
login controller
(function(){
'use strict';
angular.module('loginModule')
.controller('loginController', login);
login.$inject = [
'$log',
'$uibModal',
'$rootScope',
'storageFactory',
'loginFactory',
'$state',
'RoleStore',
'PermissionStore',
'vsmsCoreFactory'
];
function login($log, $uibModal, $rootScope, storageFactory, loginFactory, $state, RoleStore, PermissionStore, vsmsCoreFactory) {
/* jshint validthis: true */
var vm = this;
vm.loginUser = loginUser;
vm.forgotPassword = forgotPassword;
vm.errorCode = null;
PermissionStore.clearStore();
vsmsCoreFactory.getHashFunction()
.then(function(response) {
if(response) {
storageFactory.setHashFunction(response); // unknown provider
}
});
function loginUser() {
...
login controller spec
describe('loginController', function() {
var $controller;
beforeEach(module('loginModule'));
beforeEach(inject(function(_$controller_){
$controller = _$controller_;
}));
describe('vm.loginUser', function() {
it('should be defined', function() {
var loginController = $controller('loginController');
expect(loginController.loginUser).toBeDefined();
});
});
});
unit test files
'use strict';
var gulp = require('gulp');
var $ = require('gulp-load-plugins')();
var wiredep = require('wiredep');
var paths = gulp.paths;
function runTests (singleRun, done) {
var bowerDeps = wiredep({
directory: 'bower_components',
exclude: ['bootstrap-sass-official'],
dependencies: true,
devDependencies: true
});
var testFiles = bowerDeps.js.concat([
'./src/components/scripts/ui-bootstrap-custom-tpls-2.1.3.js',
'./src/app/index.js',
'./src/{app,components}/**/*.module.js',
'./src/{app,components}/**/*.factory.js',
'./src/{app,components}/**/*.controller.js',
'./src/{app,components}/**/*.spec.js'
]);
gulp.src(testFiles)
.pipe($.karma({
configFile: 'karma.conf.js',
action: (singleRun)? 'run': 'watch'
}))
.on('error', function (err) {
// Make sure failed tests cause gulp to exit non-zero
throw err;
});
}
gulp.task('test', function (done) { runTests(true /* singleRun */, done) });
gulp.task('test:auto', function (done) { runTests(false /* singleRun */, done) });
app
'use strict';
angular.module('fotaAdminPortal',
[
'ngAnimate',
'ngCookies',
'ngTouch',
'ngSanitize',
'ngResource',
'ui.bootstrap',
'ui.router',
'ui.router.stateHelper',
'pascalprecht.translate',
'utilsModule',
'loginModule',
...
])
log
Chrome 54.0.2840 (Mac OS X 10.11.6) loginController vm.loginUser should be defined FAILED
Error: [$injector:unpr] Unknown provider: storageFactoryProvider <- storageFactory <- loginController
The fotaAdminPortal module isn't loaded for the test so vsmsCoreFactory never gets registered.
Also there's no need to manually pass services into a controller as the injector will automatically do this when you create it. Passing services manually is useful when you need to pass something that isn't a globally registered service ie $scope or mock a service for a single controller instance.
Atm the mocks for the factories are just an undefined variable which will cause errors when the logic depending on them tries to call the methods that don't exist. You'll need to properly stub any methods on these mocks. But leaving those aside for the moment your test should be something like:
describe('loginController', function() {
var $controller;
beforeEach(module('fotaAdminPortal'));
beforeEach(inject(function(_$controller_){
$controller = _$controller_;
}));
describe('vm.loginUser', function() {
it('should be defined', function() {
var loginController = $controller('loginController');
expect(loginController.loginUser).toBeDefined();
});
});
});
If you want to load loginModule in isolation then you need to extract vsmsCoreFactory into another module and add that as a dependency:
angular.module('vsmsCore', [])
.factory('vsmsCoreFactory', function($http, env, $log, storageFactory) {
});
angular.module('loginModule', ['ui.bootstrap', 'permission', 'ui.router', 'vsmsCore']);
Then you can do beforeEach(module('loginModule')) instead

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