Angular $watchGroup and $watch undefined in Jasmine tests - angularjs

Whoever said that testing Angular apps is a breeze had to be joking. Since I started writing tests for our Angular application, I consider it a great success when I move from one error message to another when running karma. Most of the examples online seem to be simplified and are not really transferable to my error cases. Now, onto the current problem I have:
I have angular-mocks.js and other angular dependencies hooked up in karma.conf.js file, I have tested config block of our app (controllers and templates matching routes) and the tests are green. Now I am trying to test controller which has $watchGroup - for some bloody reason $watchGroup is undefined (and also $watch when I tried to use it) in my jasmine test. When I comment the $watchGroup out my dummy test expect(true).toBe(true) is green, but with $watchGroup code in the controller (which is working fine btw) karma console reports that $watchGroup is undefined.
This is the code in the controller:
$scope.$watchGroup([
'Message.AgeRangeMin',
'Message.AgeRangeMax',
'Message.SubscriberListFileId',
'Message.SmsSettings.SelectedSender',
'Message.EmailSettings.SelectedTemplate',
'Message.PushSettings.SelectedSenders.length',
'Message.SocialSettings.SelectedSocialNetworks.length'
], $scope.triggerUserForecast
);
$scope.triggerUserForecast = function () {
commsMgmtHttpService.GetTotalReach($scope.Message)
.then(function (data) {
$scope.UserDeliveryForecast = data;
}, function () {
$scope.UserDeliveryForecast.TotalUserReach = 0;
});
};
This is my test case:
describe('forge.communications.CommsApp', function () {
beforeEach(module('forge.communications.CommsApp'));
describe('CreateScheduledMessageController', function () {
var ctrl, $scope, $rootScope, $controller, $httpBackend;
beforeEach(function () {
inject(function (_$rootScope_, _$controller_, _$httpBackend_) {
$rootScope = _$rootScope_;
$scope = $rootScope.$new();
$httpBackend = _$httpBackend_;
$controller = _$controller_('CreateScheduledMessageController', {
$scope: $scope,
$scope: {
ModelState: new ModelState($scope)
},
$location: $location,
$modal: $modal,
$upload: $upload
});
})
});
it("dummy should be true", function () {
expect(true).toBe(true);
});
});
});
This is the Karma console error I am getting:
Chrome 40.0.2214 (Windows 7) forge.communications.CommsApp CreateScheduledMessageController dummy should be defined FAILED TypeError: undefined is not a function at new (C:/work/theforge/src/TheForge/dist/CommsApp.js:2581:12) at invoke (C:/work/theforge/src/TheForge/Scripts/angular.js:4118:17) at Object.instantiate (C:/work/theforge/src/TheForge/Scripts/angular.js:4129:23) at C:/work/theforge/src/TheForge/Scripts/angular.js:8320:28 at Object. (C:/work/theforge/src/TheForge/FrontEndTests/CommsAppTests/unit/Controllers/CreateScheduledMessage/CreateScheduledMessageController.spec.js:31:31) at Object.invoke (C:/work/theforge/src/TheForge/Scripts/angular.js:4118:17) at Object.workFn (C:/work/theforge/src/TheForge/Scripts/angular-mocks.js:2257:20) at window.inject.angular.mock.inject (C:/work/theforge/src/TheForge/Scripts/angular-mocks.js:2229:37) at Object. (C:/work/theforge/src/TheForge/FrontEndTests/CommsAppTests/unit/Controllers/CreateScheduledMessage/CreateScheduledMessageController.spec.js:22:13) Error: Declaration Location at window.inject.angular.mock.inject (C:/work/theforge/src/TheForge/Scripts/angular-mocks.js:2228:25)at Object. (C:/work/theforge/src/TheForge/FrontEndTests/CommsAppTests/unit/Controllers/CreateScheduledMessage/CreateScheduledMessageController.spec.js:22:13) Chrome 40.0.2214 (Windows 7): Executed 15 of 15 (1 FAILED) (0 secs / 0.12 secs) WARN [web-server]: 404: /forge/signalr/negotiate?clientProtocol=1.4&connectionDaChrome 40.0.2214 (Windows 7): Executed 15 of 15 (1 FAILED) (0.415 secs / 0.12 secs)
Any advice will be of much help to me.
Thanks.

It looks like I was overwriting the $controller's $scope property in jasmine test. Removing the following lines of code, following #Chandermani's advice fixed my problem.
$scope: {
ModelState: new ModelState($scope)
}

Related

Testing AngularJS + Jasmine Unexpected request

I created an application using AngularJS which you can only access after logging in.
Now I'm starting to learn how to use Jasmine.
I created a simple test (see below) just to see if everything is ok.
describe('dashboardController', function() {
beforeEach(module('mainModule'));
var $controller;
beforeEach(inject(function(_$controller_){
$controller = _$controller_;
}));
describe('demo', function() {
it('demo spec', function() {
var $scope = {};
var controller = $controller('dashboardController', { $scope: $scope });
expect($scope.foo).toEqual(1);
});
});
});
When I load the application I get "finished in 0.011s1 spec, 0 failures" which means the test went just fine.
However it is blocking me from doing anything. I cant click on the login button or anything else. I also get this error in the console:
"Error: Unexpected request: GET api/getUser
No more request expected
at $httpBackend (angular-mocks.js:1232)"
this route above is responsible for checking if the user is logged in or not.
Im new to Jasmine with AngularJS. Can someone clarify what's going on here?
EDIT: Here's my controller:
angular.module("mainModule")
.controller("dashboardController", ["$scope", "$rootScope", function ($scope, $rootScope) {
$scope.foo = 1;
}]);

Unit Testing a Controller Ionic Framework Karma

I've been beating my head against the wall trying to figure out why these tests aren't working using Karma and Jasmine to test an Ionic Controller. I'm new to ionic, karma unit testing, angularjs, and just about everything else here. Can anyone identify the reason these tests are failing?
Here's my controller:
angular.module('starter.controllers')
.controller('TemplateCtrl', function($scope) {
$scope.text = 'Hello Template';
});
Here's my test:
describe('Template Controller', function(){
var scope;
beforeEach(module('starter.controllers'));
beforeEach(inject(function($rootScope, $controller) {
scope = $rootScope.$new();
$controller('TemplateCtrl', {$scope: scope});
}));
// tests start here
it('should have scope defined', function() {
expect(scope).toBeDefined();
});
it('should have text set to Hello Template', function(){
expect(scope.text).toEqual('Hello Template');
});
});
Test results:
PhantomJS 2.1.1 (Linux 0.0.0) Template Controller should have scope defined FAILED
Expected undefined to be defined.
PhantomJS 2.1.1 (Linux 0.0.0) Template Controller should have text set to Hello Template FAILED
TypeError: undefined is not an object (evaluating 'scope.text') in controller-tests/template.tests.js (line 23)
Thank you for your time.
Phil Identified the problem, without me even posting my karma config file:
Does Karma include the files that define your starter.controllers
module as well as the TemplateCtrl controller?
Simply adding the path to the karma config file fixed my issue.
Thank you Phil!

Jasmine Angular http testing

I'm trying to test a REST API wrapped in an AngularJS service using Jasmine async testing. I know, I should be, and am, running tests on the API on the server, and using a mock $httpBackend to test the service.
but when i test this code :
'use strict';
describe('controllers: SiteCtrl', function() {
var scope, httpBackend, http, controller;
var zipcode = "94305";
beforeEach(module('ngMockE2E'));
beforeEach(module('ironridge'));
beforeEach(inject(function($rootScope, $controller, $httpBackend, $http) {
scope = $rootScope.$new();
httpBackend = $httpBackend;
controller = $controller;
http = $http;
// httpBackend.w hen('http://api.zippopotam.us/us/' + zipcode).respond({});
$controller('SiteCtrl', {
$scope: scope,
$http: $http
});
}));
it('should define more than 5 awesome things',function (){
expect(scope.nav.radio).toMatch('site');
});
it("should match a correct zip code", function() {
httpBackend.expectGET('http://api.zippopotam.us/us/' + zipcode).responde(200);
http.get('http://api.zippopotam.us/us/' + zipcode).
success(function(data) {
console.log(data);
}).
error(function(status) {
console.log(status);
});
httpBackend.flush();
});
});
but i get an error when running test
[16:01:41] all files 33.1 kB
[16:01:41] Finished 'scripts' after 1.06 s
[16:01:41] Starting 'test'...
WARN [watcher]: Pattern "/home/hpro/ironridge/src/**/*.mock.js" does not match any file.
INFO [karma]: Karma v0.12.37 server started at http://localhost:9876/
INFO [launcher]: Starting browser PhantomJS
INFO [PhantomJS 1.9.8 (Linux 0.0.0)]: Connected on socket Cfi_cbdTVW-YOCX8_BaV with id 45541989
PhantomJS 1.9.8 (Linux 0.0.0) controllers: SiteCtrl should match a correct zip code FAILED
TypeError: 'undefined' is not a function (evaluating 'httpBackend.expectGET('http://api.zippopotam.us/us/' + zipcode).responde(200)')
at /home/hpro/ironridge/src/app/site/site.controller.spec.js:28
PhantomJS 1.9.8 (Linux 0.0.0): Executed 2 of 2 (1 FAILED) (0.006 secs / 0.059 secs)
[16:01:42] 'test' errored after 1.41 s
[16:01:42] Error: 1
at formatError (/usr/lib/node_modules/gulp/bin/gulp.js:169:10)
at Gulp.<anonymous> (/usr/lib/node_modules/gulp/bin/gulp.js:195:15)
at Gulp.emit (events.js:107:17)
at Gulp.Orchestrator._emitTaskDone (/home/hpro/ironridge/node_modules/gulp/node_modules/orchestrator/index.js:264:8)
at /home/hpro/ironridge/node_modules/gulp/node_modules/orchestrator/index.js:275:23
at finish (/home/hpro/ironridge/node_modules/gulp/node_modules/orchestrator/lib/runTask.js:21:8)
at cb (/home/hpro/ironridge/node_modules/gulp/node_modules/orchestrator/lib/runTask.js:29:3)
at removeAllListeners (/home/hpro/ironridge/node_modules/karma/lib/server.js:215:7)
at Server.<anonymous> (/home/hpro/ironridge/node_modules/karma/lib/server.js:226:9)
at Server.g (events.js:199:16)
Thanks for help :D
You cannot do E2E tests with httpBackend.
First option, to forfit E2E tests, and make tests for $httpBackend, without e2e tests and run it from grunt running grunt test. Which would be unit tests.
Second option, which you can mix with first is to create separate file for E2E tests, also in Jasmine syntax and run it independly with protractor.
In edited code, use flush before calling handle.success.
Flushing means that get operation goes into action.
Instead of:
$http.get('http://api.zippopotam.us/us/' + zipcode).
then(handler.success,handler.error);
httpBackend.flush();
Write:
var prom = $http.get('http://api.zippopotam.us/us/' + zipcode);
httpBackend.flush();
prom.then(handler.success,handler.error);
It's possible that handler.success is called before flush is made.

$rootScope is undefined when testing with Karma

I am trying to write some unit tests for an angular project. I've followed some examples about testing a controller but I always end up with the same problem: $rootScope seems to be undefined.
My test.js:
describe("Unit: BodyCtrl", function() {
var scope;
beforeEach(function($rootScope, $controller) {
scope = $rootScope.$new();
});
it('foo should be foo', function() {
expect("foo").toBe("foo");
});
})
and the error:
TypeError: Cannot read property '$new' of undefined'
I have included all the angular files in karma.conf.js. Could it cause problems that I have mounted the main application through sshfs from a server to my local Ubuntu 14.04 and Karma is installed on the said local machine?
You need to inject it. Use inject in beforeEach, otherwise it is a mere variable defined in your function scope which is not defined with any value.
Example:-
beforeEach(inject(function($rootScope, $controller) {
scope = $rootScope.$new();
ctrl = $controller('yourcontroller', {
$scope: $rootScope.$new()
});
}));

How does AngularJS dependency injection work with controller testing?

I'm trying to write tests for some AngularJS code, but can't even get a hello world to run. Suppose my code looks like this:
var myApp = angular.module("myApp", [])
myApp.controller("MyCtrl", function ($scope) {
$scope.hello = "world"
})
Then the angular docs here suggest that something like this (using jasmine) should work:
describe("my controller", function () {
it("should say hello", function () {
var $scope
inject(function ($rootScope, $controller) {
$scope = $rootScope.$new()
$controller('MyCtrl', {$scope: $scope})
})
expect($scope.hello).toBe("world")
}
}
Unfortunately, inject does not exist, and there are no hints in the docs as to where to get it. Thus the approach in the docs doesn't work.
Looking slightly farther afield, we find $injector, which can be created by angular.injector. From those docs, it's fairly clear that inject(f) should be $injector.invoke(f). So we stick this at the top of our code and make the change:
$injector = angular.injector(["myApp"])
Unfortunately, this gives the error "Uncaught Error: Unknown provider: $controllerProvider from myApp", which my google-fu seems unable to elucidate.
I had been using this ($injector) previously when I was only testing services, and it works perfectly. It is only when mixed with a controller definition that I get the error. It can handle the controller definition, or the $injector, but not both. To me this suggests some kind of priority conflict or double-initialization, but I can't figure it out.
So what does the "Unknown provider..." error mean, and how do I get my hello world controller test working? If someone can help sort me out, that would be great.
I created for you an skeleton you could use for that concrete controller.
describe('my controller', function() {
var $scope;
beforeEach(module('app'));
beforeEach(inject(function($rootScope, $controller) {
$scope = $rootScope.$new();
$controller('MyCtrl', { $scope: $scope });
}));
it('should contain world', function() {
expect($scope.hello).toBe('world');
});
});
Before each test, you inject your app module and before each test, you creates a new scope and instantiates your controller. Then you just need to write as much tests as you need.
We create a new scope in every tests to have a fresh state. We don't want to modify some $scope in one test and then have another one failing because you modified your $scope earlier.
Plunker: http://plnkr.co/edit/4hHqdsvnVwyUhiezpdhW?p=preview
Write a comment if you have any question.

Resources