How to invoke spyOn on a scope function - angularjs

I have the following jasmine spec.
describe('ViewMeetingCtrl', function () {
var $rootScope, scope, $controller , $q ;
beforeEach(angular.mock.module('MyApp'));
beforeEach(inject(function ($rootScope, $controller ) {
scope = $rootScope.$new();
createController = function() {
return $controller('ViewMeetingCtrl', {
$scope: scope,
meeting : {}
});
};
}));
it('the meeting type should be equal to an object', function () {
var controller = new createController();
//some assertion
});
});
Following is my ViewMeetingCtrl.js
(function () {
'use strict';
angular.module('MyApp').controller('ViewMeetingCtrl', ViewMeetingCtrl);
ViewMeetingCtrl.$inject = ['$scope', '$state', '$http', '$translate', 'notificationService', 'meetingService', '$modal', 'meeting', 'attachmentService'];
function ViewMeetingCtrl($scope, $state, $http, $translate, notificationService, meetingService, $modal, meeting, attachmentService) {
$scope.meeting = meeting;
$scope.cancelMeeting = cancelMeeting;
function cancelMeeting(meetingId, companyId) {
meetingService.sendCancelNotices(companyId, meetingId)
.success(function () {
$state.go('company.view');
});
}
//more code
}
})();
My question that how do i invoke the spyOn (or any other jasmine spies related method) method on the above cancelMeeting() so that i can mock the method calls , returns etc. I did the following
describe('ViewMeetingCtrl', function () {
var $rootScope, scope, $controller , $q ;
beforeEach(angular.mock.module('MyApp'));
beforeEach(inject(function ($rootScope, $controller ) {
scope = $rootScope.$new();
createController = function() {
return $controller('ViewMeetingCtrl', {
$scope: scope,
meeting : {}
});
};
}));
it('the meeting type should be equal to an object', function () {
spyOn(scope, 'cancelMeeting');//cancelMeeting is inside the scope so did like this
var controller = new createController();
});
});
but i get the following output
Firefox 37.0.0 (Windows 8.1) ViewMeetingCtrl the meeting type should be equal to an object FAILED
Error: cancelMeeting() method does not exist in C:/Users/Work/MyApp/Tests/node_mo
dules/jasmine-core/lib/jasmine-core/jasmine.js (line 1895)
is the way i am invoking spyOn is wrong or any another syntax's am i missing ?. Or do i missing something fundamental here ?

The cancelMeeting function is not added to the scope until the controller is created. So I think you just need to reverse the lines in your test code:
it('the meeting type should be equal to an object', function () {
var controller = new createController();
spyOn(scope, 'cancelMeeting');
});

Your test code looks good. I think you just have to switch the order of your assignments. First define cancelMeeting and than assign it.
function cancelMeeting(meetingId, companyId) {
meetingService.sendCancelNotices(companyId, meetingId)
.success(function () {
$state.go('company.view');
});
}
$scope.cancelMeeting = cancelMeeting;
Or just:
$scope.cancelMeeting = function(meetingId, companyId) {
meetingService.sendCancelNotices(companyId, meetingId)
.success(function () {
$state.go('company.view');
});
}

Related

Unit test $mdDialog angular material

I called one $mdDialog inside a function. I want to unit-test $mdDialog ok and cancel cases.
The below is my controller code (app.controller.js).
(function () {
'use strict';
app.controller('AppCtrl', AppCtrl);
AppCtrl.$inject = ['$scope', '$mdDialog'];
function AppCtrl($scope, $mdDialog) {
$scope.saveEntry = function (ev) {
var confirm = $mdDialog.prompt()
.title('Save Entry')
.textContent('If you want, you can add a description to explain what you changed.')
.placeholder('Version Description')
.ariaLabel('Version Description')
.initialValue('')
.targetEvent(ev)
.ok('Save')
.cancel('Cancel');
$mdDialog.show(confirm).then(function (result) {
$scope.status = true;
}, function () {
$scope.status = false;
});
};
}
})();
The following is the spec code (app.controller.spec.js) :
describe('Unit test AppController: mdDialog', function () {
var $controller, $mdDialog;
beforeEach(function () {
module('App');
inject(function (_$controller_, _$mdDialog_) {
$controller = _$controller_;
$mdDialog = _$mdDialog_;
});
});
it(': Opened', function () {
var $scope = {};
var controller = $controller('AppCtrl', { $scope: $scope });
var $mdDialogOpened = false;
$mdDialog.show = jasmine.createSpy().and.callFake(function () {
$mdDialogOpened = true;
});
$scope.saveEntry();
$scope.$digest();
expect($mdDialog.show).toHaveBeenCalled;
expect($mdDialogOpened).toBe.true;
});
});
when I running the above code I'm getting the following error:
TypeError: Cannot read property 'then' of undefined
I referred this GitHub issue https://github.com/angular/material/issues/1482. But I'm not getting solution for my problem
Thanks in advance
The problem is that you are injecting one version of $mdDialog, and trying to test on another one.
You could try something like this:
describe('Unit test AppController: mdDialog', function () {
var ctrl, mdDialog, scope;
beforeEach(function () {
module('App');
inject(function ($rootScope, $controller, $mdDialog) {
scope = $rootScope.$new();
mdDialog = $mdDialog; //keep the reference, for later testing.
spyOn(mdDialog, 'show');
mdDialog.show.and.callFake(function () {
return {
then: function (callBack) {
callBack(true); //return the value to be assigned.
}
}
});
ctrl = $controller('AppCtrl',{$scope:scope, $mdDialog:mdDialog}); //Inject the dependency
});
});
it(': Opened', function () {
scope.saveEntry(); //exercise the method.
scope.$digest();
expect(mdDialog.show).toHaveBeenCalled();
expect(scope.status).toBe(true);
});
});
Something very similar should work.
hope this help.

Why am I losing my invocation context when testing code inside of a promise (angular + jasmine)

I have simple controller where I want to test mechanics inside of a promise (in this case, I want to test that foo was called when I run bar. Here's my controller:
angular.module('myModule', [])
.controller('MyCtrl', function ($q) {
var myPromise = $q.when();
this.foo = function () {
console.log('running foo');
};
this.bar = function () {
myPromise.then(function () {
this.foo();
});
};
});
And here's my jasmine test:
describe('MyCtrl', function () {
var $controller, $scope, $q;
beforeEach(inject(function ($rootScope, _$q_, _$controller_) {
$controller = _$controller_;
$q = _$q_;
$scope = $rootScope.$new();
}));
describe('bar function', function () {
it('should call the foo function', function () {
var controller = $controller('MyCtrl', { $q: $q });
spyOn(controller, 'foo');
controller.bar();
$scope.$digest();
expect(controller.foo).toHaveBeenCalled();
});
});
});
When I run this test, I get this error:
TypeError: 'undefined' is not an object (evaluating 'this.foo')
It seems that inside the then() function block, I lose invocation context referring to the controller. When the test runs and hits this.foo(), this is undefined.
'this' doesn't contain an attribute 'foo' because the context (to the outer scope) is not bound.
You could do one of the following:
1.
this.bar = function() {
var that = this;
myPromise.then(function () {
that.foo();
});
};
2.
this.bar = function() {
function onSuccess() { this.foo(); }
myPromise.then(onSuccess.bind(this));
};

How to test .then(function in Unit Testing AgularJS with jasmine

I am not clear how to use SpyOn in Unit Testing...
I have the following controller
(function () {
'use strict';
angular.module('otpConfigureDatasets').controller('otpActivityCardController', otpActivityCardController);
otpActivityCardController.$inject = ['$location', '$state', 'otpWebMapApp', 'otpWMDeltaTracker', 'otpWMStateCache', '$scope', '$timeout', 'otpActivityCardService', 'otpControlCenterData'];
function otpActivityCardController($location, $state, otpWebMapApp, otpWMDeltaTracker, otpWMStateCache, $scope, $timeout, otpActivityCardService, otpControlCenterData) {
var vm = this;
vm.cards = [];
otpActivityCardService.getActivityCards().then(function (resolve) {
vm.cards = resolve;
});
//.....Some code ....
})();
I need to test the GetActivityCards().then(function ...
I tried test it using the code below
'use strict';
describe('Test controller (activityCard) in Page MyDatasets', function() {
var MainCtrl, $state, scope, otpWebMapApp, otpWMDeltaTracker, otpWMStateCache, otpActivityCardService, otpControlCenterData;
var card;
beforeEach(function() {
module('otpConfigureDatasets');
});
beforeEach(inject(function ($controller, $rootScope, _$state_, _otpWebMapApp_, _otpWMDeltaTracker_, _otpWMStateCache_, _otpActivityCardService_, _otpControlCenterData_) {
scope = $rootScope.$new();
scope.$parent = { $parent: { menuParentGroupClick: function menuParentGroupClick() { } } };
MainCtrl = $controller('otpActivityCardController', {
$scope: scope
});
otpWebMapApp = _otpWebMapApp_;
otpWMDeltaTracker = _otpWMDeltaTracker_;
otpWMStateCache = _otpWMStateCache_;
otpActivityCardService = _otpActivityCardService_;
otpControlCenterData = otpControlCenterData;
}));
it('Test Function', function() {
spyOn(otpActivityCardService, 'getActivityCards');
expect(otpActivityCardService.getActivityCards).toHaveBeenCalled();
});
});
But I am getting this error:
Expected spy getActivityCards to have been called.
Error: Expected spy getActivityCards to have been called.
What is wrong?
You created a spy to the "getActivityCards" function, but you didn't call it in your test (unless you hid this line of code from the example).
When you create a Jasmine Spy to a function, you are only "watching" this function, you can check if it was called, you can mock the return values of it, you can check the parameters of a call to it, i.e, you can check a lot of things about the call history of the function, but you still need to explicity make a call to the function (or to a function in your controller that calls the spied function from it).
So you are spying the Service, and you are testing the Controller, your test should look something like:
it('Test Function', function() {
spyOn(otpActivityCardService, 'getActivityCards');
otpActivityCardService.getActivityCards();
expect(otpActivityCardService.getActivityCards).toHaveBeenCalled();
});
On a side note, to be more testable, your controller should encapsulate your service call in a function in your controller, like:
(function () {
'use strict';
angular.module('otpConfigureDatasets').controller('otpActivityCardController', otpActivityCardController);
otpActivityCardController.$inject = ['$location', '$state', 'otpWebMapApp', 'otpWMDeltaTracker', 'otpWMStateCache', '$scope', '$timeout', 'otpActivityCardService', 'otpControlCenterData'];
function otpActivityCardController($location, $state, otpWebMapApp, otpWMDeltaTracker, otpWMStateCache, $scope, $timeout, otpActivityCardService, otpControlCenterData) {
var vm = this;
vm.cards = [];
vm.getCards = function () {
otpActivityCardService.getActivityCards().then(function (resolve) {
vm.cards = resolve;
});
}
vm.getCards();
//.....Some code ....
})();
So you could create a test that really tested a function in your controller (because the way you are describing your test case, it really should be a Service test only)
it('Better test case', function() {
spyOn(otpActivityCardService, 'getActivityCards');
MainCtrl.getCards();
expect(otpActivityCardService.getActivityCards).toHaveBeenCalled();
});

AngularJS E2E/functional test controller

I just start with tests in AngularJS. Please help me to fix it.
My cript
angular.module('test', [])
.controller('ctrl', ['$scope', 'svc', function ($scope, svc) {
$scope.data = [];
svc.query()
.then(function (data) {
$scope.data = data;
});
}]);
and test spec
describe('ctrl', function () {
var ctrl, scope, svc, def, data = [{name: 'test'}];
beforeEach(module('test'));
beforeEach(inject(function($controller, $rootScope, $q) {
svc = {
query: function () {
def = $q.defer();
return def.promise;
}
};
var a=jasmine.createSpy(svc, 'query');
scope = $rootScope.$new();
controller = $controller('ctrl', {
$scope: scope,
svc: svc
});
}));
it('should assign data to scope', function () {
def.resolve(data);
scope.$digest();
expect(svc.query).toHaveBeenCalled();
expect(scope.data).toBe(data);
});
});
It fail:Error: Expected a spy, but got Function. in http://cdn.jsdelivr.net/jasmine/2.0.0/jasmine.js (line 2125). Can you help me
You are getting that error because its failing on expect method. expect method is expecting a spy to be passed in but its not. To fix this problem do:
spyOn(svc, 'query').andCallThrough();
You're creating a spy using createSpy(), which returns a function you can spy on, but you nere use it. You're making your life more complex than it should be. Just let angular inject the real service, and spy on its query() function. Also, use $q.when() to create a resolved promise.
describe('ctrl', function () {
var scope, svc;
var data = [{name: 'test'}];
beforeEach(module('test'));
beforeEach(inject(function($controller, $rootScope, $q, _svc_) {
svc = _svc_;
spyOn(svc, 'query').andReturn($q.when(data));
scope = $rootScope.$new();
$controller('ctrl', {
$scope: scope,
});
}));
it('should assign data to scope', function () {
scope.$digest();
expect(svc.query).toHaveBeenCalled();
expect(scope.data).toBe(data);
});
});

Unit Testing Controller Mocking Promises Angularjs

I have the following Controller in my app:
angular.module('newradarApp').controller('ProfileController', ['$scope', '$log', '$auth', 'userRestService', function ($scope, $log, $auth, userRestService) {
/**
* loading ui toggle ativator
*/
$scope.userLoaded = false;
/**
* #returns {*} Checks (In combination with satellizer) the contextual authentication state
*/
$scope.userIsLoggedIn = function () {
return $auth.isAuthenticated();
};
//$scope.welcomeMsg = 'Welkom, ';
/**
* Holds the instance of the user's service
*/
var userServiceInstance = userRestService.obtainPersonalInformation().then(function (userData) {
$log.debug('::ReST-obtainPersonalInformation Got user data:', JSON.stringify(userData));
$scope.userName = userData.firstName;
$scope.fullName = userData.firstName + ' ' + userData.lastName;
$scope.picture = encodeURI(userData.profilePicture);
$scope.userLoaded = true;
});
}]);
I wanted to test this funtionality with Jasmine and I tried this test in this way:
'use strict';
describe('Controller: ProfileController', function () {
// load the controller's module
beforeEach(module('newradarApp'));
var ProfileController, scope, mockUserrest, def;
// Initialize the controller and a mock scope
beforeEach(inject(function ($controller, $rootScope, $q) {
mockUserrest = {
obtainPersonalInformation: function () {
def = $q.defer();
return def.promise;
}
};
spyOn(mockUserrest, 'obtainPersonalInformation').andCallThrough();
scope = $rootScope.$new();
ProfileController = $controller('ProfileController', {
$scope: scope
});
}));
it('should assign data to scope', function () {
def.resolve(userdata);
scope.$digest();
expect(mockUserrest.obtainPersonalInformation).toHaveBeenCalled();
expect(scope.userName).toBe(userdata);
});
});
Then I tried this other test using other way for mocking the service:
'use strict';
describe('Controller: ProfileController', function () {
// load the controller's module
beforeEach(angular.mock.module('newradarApp'));
var controller, scope, rootScope, mockUserrest, def;
// Initialize the controller and a mock scope
beforeEach(inject(function ($controller, $rootScope, $q) {
rootScope = $rootScope;
scope = $rootScope.$new();
controller = $controller;
mockUserrest = {
obtainPersonalInformation: function () {
def = $q.defer();
def.resolve(userdata);
return def.promise;
}
}
}));
it('should assign data to scope', function () {
controller("ProfileController", {$scope: scope, userRestService: mockUserrest});
scope.$digest();
expect(scope.username).toBe(userdata)
});
});
None of each passed so any hint what I am doing wrong please?
Your expect call seems to be a little odd
expect(scope.userName).toBe(userdata);
So here, provided you don't mind injecting the whole service (you don't have to mock it)
'use strict';
describe('Controller: ProfileController', function () {
var obtainDefer, scope;
var $rootScope;
beforeEach(angular.module('newradarApp'));
beforeEach(inject(function($controller, $rootScope, $q, userRestService) {
$rootScope = _$rootScope_;
obtainDefer = $q.defer();
scope = $rootScope.$new();
spyOn(userRestService, 'obtainPersonalInformation').and.returnValue(obtainDefer.promise);
$controller('ProfileController', {$scope: scope});
}));
it('should assign the data from the scope', function() {
obtainDefer.resolve({firstName: 'alfred'});
$rootScope.$digest();
expect(scope.userName).toEqual('alfred');
});
});

Resources