Angular Jasmine Unit Testing: error [$injector:modulerr] - angularjs

I'm to setup standalone jasmine testing. I believe I have everything setup correctly. When I run my first test:
describe('Logon Controller', function() {
var controller, $scope;
beforeEach(module("app"));
//beforeEach(inject(function ($rootScope, $controller) {
// scope = $rootScope.$new();
// contoller = $controller('logonCtrl', {
// $scope: scope
// });
//}));
beforeEach(inject(function ($rootScope) {
scope = $rootScope.$new();
}));
//it('should say hello', function ($controller, $rootScope) {
// //var scope = $rootScope.$new();
// var controller = $controller('logonCtrl', { $scope: $scope });
// //expect(angular.isFunction(scope.get)).toEqual("Hello There :)");
// expect(scope.Hello()).toEqual("Hello There :)");
// expect($scope.Hello).toBeDefined();
//});
it('should say hello', inject(function ($controller, $rootScope) {
//var scope = $rootScope.$new();
var controller = $controller('logonCtrl', {$scope: $scope});
//expect(angular.isFunction(scope.get)).toEqual("Hello There :)");
//expect(scope.Hello()).toEqual("Hello There :)");
expect($scope.Hello).toBeDefined();
}));
});
I get this error message: Error: [$injector:modulerr] http://errors.angularjs.org/1.3.11/$injector/modulerr?p0=app.............
Here is my app module config.
var app = angular.module('app', ['ui.router','ngMaterial']);
Here is my controller:
app.controller('logonCtrl', ['$scope', '$state', '$interval', function ($scope, $state, $interval) {
$scope.hello = function() {};
});
]);
I am using the standalone version of Jasmine 2.2.0. I have no clue what the issue could be, everything I have done is pretty basic stuff. Any help would be very much appreciated.
UPDATE: here is what my SpecRunner.html looks like:
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title>Jasmine Spec Runner v2.2.0</title>
<link rel="shortcut icon" type="image/png" href="JasmineUnitTest/lib/jasmine-2.2.0/jasmine_favicon.png">
<link rel="stylesheet" href="JasmineUnitTest/lib/jasmine-2.2.0/jasmine.css">
<script type="text/javascript" src="JasmineUnitTest/lib/jasmine-2.2.0/jasmine.js"></script>
<script type="text/javascript" src="JasmineUnitTest/lib/jasmine-2.2.0/jasmine-html.js"></script>
<script type="text/javascript" src="JasmineUnitTest/lib/jasmine-2.2.0/boot.js"></script>
<script type="text/javascript" src="JasmineUnitTest/lib/jasmine-2.2.0/angular.min.js"></script>
<script type="text/javascript" src="JasmineUnitTest/lib/jasmine-2.2.0/angular-mocks.js"></script>
<!-- include source files here... -->
<script type="text/javascript" src="js/controllers/logonCtrl.js"></script>
<!-- include spec files here... -->
<script type="text/javascript" src="JasmineUnitTest/specs/logonController-spec.js"></script>
</head>
<body>
</body>
</html>

It looks like the $injector fails to find some of your dependencies. Are you sure the script containing your controller is properly loaded ?

Related

How to declare and use modules, controllers, services and bootstrap manually in Angular?

i'm trying to build an AngularJS app that has one Controller, one Service and that Bootstraps manually (no ng-app). The problem is that I keep having an error :
Argument 'AppController' is not a function, got string
HTML
<!DOCTYPE HTML>
<html xmlns:ng="http://angularjs.org">
<head>
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.5.0-rc.0/angular.min.js" type="text/javascript"></script>
<script src="inc/bootstrap.js" type="text/javascript"></script>
<script src="inc/controllers.js" type="text/javascript"></script>
<script src="inc/services.js" type="text/javascript"></script>
</head>
<body ng-controller="AppController">
[...]
</body>
</html>
bootstrap.js
angular.element(document).ready(function() {
angular.bootstrap(document, ['htmlControllerApp']);
});
controllers.js
angular.module('htmlControllerApp', [])
.controller('AppController', 'ConnectionService', ['$scope', function ($scope, ConnectionService) {
// Code
}]);
services.js
angular.module('htmlControllerApp')
.factory('ConnectionService', ['$rootScope', function ($rootScope) {
// Code
}]);
Thanks
EDIT - SOLUTION
In controllers.js, use instead :
angular.module('htmlControllerApp')
.controller('AppController', ['$scope', 'ConnectionService', function ($scope, ConnectionService) {
// Code
}]);
In bootstrap.js, as a "good practice" :
angular.module('htmlControllerApp', []);
angular.element(document).ready(function() {
angular.bootstrap(document, ['htmlControllerApp']);
});
This will create the module just once in bootstrap.js and AngularJS will try to retrieve it in controllers.js and services.js.
This is the page you're looking for.
https://docs.angularjs.org/api/ng/function/angular.bootstrap
and change this
angular.module('htmlControllerApp', [])
.controller('AppController', 'ConnectionService', ['$scope', function ($scope, ConnectionService) {
// Code
}]);
to this ->
angular.module('htmlControllerApp', [])
.controller('AppController', ['$scope', 'ConnectionService', function ($scope, ConnectionService) {
// Code
}]);

Error injecting Angular 1.4.7 cookie service

I've researched this online but cannot get it to work.
I followed this online doc below to inject the $cookies service into my login controller, but I keep getting error: [$injector:modulerr]
cookie injection
(function () {
'use strict';
angular.module('rage', ['ngCookies']).controller('LoginCtrl',
['$rootScope', '$scope', '$cookies', '$modalInstance', '$q', 'datacontext', 'userService', authenticate]);
function authenticate($rootScope, $scope, $cookies, $modalInstance, $q, datacontext, userService) {
var login = this;
// OK,CANCEL CLICK EVENTS FROM MODAL !!!
$scope.ok = function () {
var user = {userId: login.userId, pswd: login.pswd};
$modalInstance.close(user);
};
$scope.cancel = function () {
$modalInstance.dismiss('cancel');
};
}
})();
I've upgraded Angular from 1.3.5 to 1.4.7 and my index.html has :
<!-- jQuery and Bootstrap -->
<script src="Scripts/jquery/jquery.min.js"></script>
<script src="Scripts/plugins/jquery-ui/jquery-ui.min.js"></script>
<script src="Scripts/bootstrap/bootstrap.min.js"></script>
<!-- Angular scripts-->
<!--<script src="Scripts/angular/angular-1.3.5.min.js"></script>-->
<script src="Scripts/angular/angular-1.4.7.min.js"></script>
<script src="Scripts/angular/angular-sanitize.min.js"></script>
<script src="Scripts/angular-ui-router/angular-ui-router.min.js"></script>
<script src="Scripts/angular/angular-cookies-1.4.7.min.js"></script>
<!-- angular-ui-bootstrap -->
<!--<script src="Scripts/bootstrap/ui-bootstrap-tpls-0.11.0.min.js"></script>-->
<script src="Scripts/bootstrap/ui-bootstrap-tpls-0.13.0.min.js"></script>
Advice is greatly appreciated...
Bob
ngCookies is an external module: you have to include angular-cookie.js script in the index.
Apart from that, I see that you injected $modalInstance, which is probably the service from ui-bootstrap. You will have to include also angular-ui-bootstrap script, and add the module as a dependency.
To sum up, your index.html should look like:
<script src="scripts/.../angular.js"></script>
<script src="scripts/.../angular-cookie.js"></script>
<script src="scripts/.../ui-bootstrap-tpls.js"></script>
And the module declaration:
angular.module('rage', ['ngCookies', 'ui.bootstrap'])

Inject $log into every controller and service

Is there any way to inject AngularJS's $log into every service and controller? It just feels a little redundant specifying it for every one.
Another way you could do it is to add a method to the rootScope, and then access it through $scope.$root in your controllers, thus avoiding another injection. I don't know if it is as bad as globals.
testapp.js
(function(){
'use strict';
angular.module('app', [])
.run(function($rootScope, $log) {
$rootScope.log = function(msg){
$log.info(msg);
}
})
.controller('LogCtrl', ['$scope', function LogCtrl($scope) {
$scope.logThis = function(msg){
$scope.$root.log(msg);
};
}]);
})();
test.html
<!doctype html>
<html lang="en">
<head>
<meta charset="UTF-8">
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.3.0-beta.5/angular.min.js"></script>
<script src="testapp.js"></script>
</head>
<body ng-app="app">
<div ng-controller="LogCtrl">
<p>Enter text and press the log button.</p>
Message:
<input type="text" ng-model="message"/>
<button ng-click="logThis(message)">log</button>
</div>
</body>
</html>
Injecting it seems impossible to me without defining it in the function parameters. But you can make it available:
var $log;
app.run(['$log',function(logService) {
$log = logService;
}]);
app.controller('MainCtrl', function($scope, myService) {
$log.warn('Controlling');
});
app.service('myService', function() {
$log.warn('Ha!');
return {};
});
http://plnkr.co/edit/Zwnay7dcMairPGT0btmC?p=preview
Another way would be to set it as a global variable (window.$log), but I wouldn't do that.
Here's a solution for those who think that using the $rootscope requires too much fuss: add the $log to the angular object.
angular.module('myModule')
.run(['$log', function($log) {
angular.log = $log;
}]);
Then when you create your controllers, no $log is required.
angular.module('myModule')
.controller('MyController', MyController);
MyController.$inject = []; // <-- see, no $log required!
function MyController() {
angular.log.info("Hello world");
}
You could even take it a step further and add angular.info = $log.info if you wish to shorten it a bit more.

Unable to use $compile in AngularJS

I'm working on an AngularJS app that uses a custom directive. I'm also trying to use unit testing in my app. So, I'm trying to leverage Jasmine for that. Currently, My unit test is the problem. Currently, it looks like the following:
myDirective.spec.js
describe('Directive: myDirective', function () {
describe('Function: myProcedure', function () {
it('should word', function ($rootScope, $compile) {
var e = angular.element('<div><br /></div>');
console.log($compile);
$compile(e)($rootScope);
expect(true).toBe(true);
});
});
});
This test is throwing an exception. When the console.log line is ran, it prints: 'undefined'. The next line throws an error to Jasmine that says: 'TypeError: undefined is not a function'.
Its like I'm not injecting the $compile service. However, I believe I'm doing so. My test runner looks like the following:
<html ng-app="testApp">
<head>
<title></title>
<script type="text/javascript" src="//ajax.googleapis.com/ajax/libs/angularjs/1.2.13/angular.min.js"></script>
<script type="text/javascript" src="//cdnjs.cloudflare.com/ajax/libs/jasmine/2.0.0/jasmine.js"></script>
<script type="text/javascript" src="//cdnjs.cloudflare.com/ajax/libs/jasmine/2.0.0/jasmine-html.js"></script>
<script type="text/javascript" src="//cdnjs.cloudflare.com/ajax/libs/jasmine/2.0.0/boot.js"></script>
<script type="text/javascript" src="index.html.js"></script>
<script type="text/javascript" src="directives/myDirective.js"></script>
<script type="text/javascript" src="tests/unit/myDirective.spec.js"></script>
<link rel="stylesheet" href="//cdnjs.cloudflare.com/ajax/libs/jasmine/2.0.0/jasmine.css" />
</head>
<body>
run tests
</body>
</html>
Why cannot I not run this basic test? What am I doing wrong?
First you must add angular mocks:
<script src="//ajax.googleapis.com/ajax/libs/angularjs/1.2.13/angular-mocks.js">
</script>
then load the modules and inject $compile/$rootScope in beforeEach blocks:
describe('Directive: myDirective', function () {
var $compile;
var $rootScope;
beforeEach(module('testApp'));
beforeEach(inject(function(_$compile_, _$rootScope_){
$compile = _$compile_;
$rootScope = _$rootScope_;
}));
describe('Function: myProcedure', function () {
it('should word', function () {
var e = angular.element('<div><br /></div>');
$compile(e)($rootScope);
expect(true).toBe(true);
});
});
});
Check unit testing docs: http://docs.angularjs.org/guide/dev_guide.unit-testing#directives
The it() function provided by jasmine won't do any injection for you, so you need to inject your angular components into your tests. Modify your test as follows:
describe('Directive: myDirective', function () {
describe('Function: myProcedure', function () {
it('should word', function () {
inject(function ($rootScope, $compile) {
var e = angular.element('<div><br /></div>');
//rest of code goes here
});
You also need a reference to the angular.mocks library.
I have come across this problem just now and this is what made my day (in the very end of it actually).just add "module('ng');" along with module('yourAppModule'). It should work.

Angular.js e2e test HTTP requests to external service

I'm trying to make some basic tests on directives on an e2e scenario. The code works just fine
and I can render the new element to the browser. Here the code I'm using.
Here the directive code.
'use strict';
var directives = angular.module('lelylan.directives', [])
directives.directive('device', ['Device', function(Device) {
var definition = {
restrict: 'E',
replace: true,
templateUrl: 'js/templates/device.html',
scope: { id: '#' }
};
definition.link = function postLink(scope, element, attrs) {
scope.$watch('id', function(value){
var device = Device.get({ id: scope.id }, function() {
scope.device = device;
});
});
};
return definition
}]);
Here the Device service code.
// Service Device
'use strict';
angular.module('lelylan.services', ['ngResource']).
factory('Device', ['$resource', '$http', function($resource, $http) {
var token = 'df39d56eaa83cf94ef546cebdfb31241327e62f8712ddc4fad0297e8de746f62';
$http.defaults.headers.common["Authorization"] = 'Bearer ' + token;
var resource = $resource(
'http://localhost:port/devices/:id',
{ port: ':3001', id: '#id' },
{ update: { method: 'PUT' } }
);
return resource;
}]);
Here the app code.
'use strict';
angular.module('lelylan', ['lelylan.services', 'lelylan.directives'])
And here the index.html.
<!doctype html>
<html lang="en" ng-app="lelylan">
<head>
<meta charset="utf-8">
<title>Lelylan Components</title>
<link rel="stylesheet" href="css/app.css"/>
</head>
<body>
<device id="50c61ff1d033a9b610000001"></device>
<!-- In production use: <script src="//ajax.googleapis.com/ajax/libs/angularjs/1.0.4/angular.min.js"></script> -->
<script src="lib/angular/angular.js"></script>
<script src="lib/angular/angular-resource.js"></script>
<script src="js/app.js"></script>
<script src="js/services.js"></script>
<script src="js/directives.js"></script>
</body>
</html>
After reading the Angular documentation and trying different solutions I came up with the following
test where I try mock my Backend requests. The problem is that the request still hits the real service. It
looks like I'm not able to intercept the requests.
// e2e test
'use strict';
describe('directives', function() {
var resource = { id: '1', uri: 'http://localhost:3001/devices/1' };
var myAppDev = angular.module('myAppDev', ['lelylan', 'ngMockE2E']);
myAppDev.run(function($httpBackend) {
$httpBackend.when('GET', 'http://localhost:3001/devices/1').respond(resource);
$httpBackend.when('GET', /\/templates\//).passThrough();
});
beforeEach(function() {
browser().navigateTo('../../app/index.html');
});
describe('when renders a device', function() {
it('renders the title', function() {
expect(element('.device .name').text()).toEqual('Closet dimmer');
});
it('renders the last time update', function() {
expect(element('.device .updated-at').text()).toEqual('2012-12-20T18:40:19Z');
})
});
});
I think I'm missing some configurations, but I can't really understand which ones.
Thanks a lot.
Reading the the last comment in this question I came with the final solution.
Actually I was missing one important step, due to the fact that I had to use an HTML file that uses the mocked application. Let's make the code speak.
1) I've created an HTML file with the test environment. The main difference is related to the fact that I've set the ng-app=test and I've added two new JS files. The first is /test/e2e/app-test.js and is where I've created the test module and the second is /test/lib/angular-mocks.js.
<!doctype html>
<html lang="en" ng-app="test">
<head>
<meta charset="utf-8">
<title>Lelylan Test</title>
<link rel="stylesheet" href="css/app.css"/>
</head>
<body>
<device id="1"></device>
<script src="lib/angular/angular.js"></script>
<script src="lib/angular/angular-resource.js"></script>
<script src="js/app.js"></script>
<script src="js/settings.js"></script>
<script src="js/services.js"></script>
<script src="js/controllers.js"></script>
<script src="js/filters.js"></script>
<script src="js/directives.js"></script>
<!-- Test application with mocked requests -->
<script src="../test/e2e/app-test.js"></script>
<script src="../test/lib/angular/angular-mocks.js"></script>
</body>
</html>
Now, lets see how we implement the test module. In here I define a module that is exactly as my main module (lelylan), with the addition of ngMockE2E that lets you access to $httpBackend and mock the HTTP requests.
'use strict';
var resource = { id: '1', uri: 'http://localhost:3001/devices/1' };
var test = angular.module('test', ['lelylan', 'ngMockE2E']);
test.run(function($httpBackend) {
$httpBackend.when('GET', 'http://localhost:3001/devices/1').respond(resource);
$httpBackend.when('GET', /\/templates\//).passThrough();
});
Nothing more. Run scripts/e2e-test.sh and you're done.

Resources