My service looks like this:
app.service('foo',function($q){
this.fn3 = function(){
var deferred = $q.defer();
return deferred.promise;
}
});
My spec document is as below:
describe('Testing: MainCtrl', function() {
var $scope = null;
var ctrl = null;
var mockfoo;
var deferred;
var $q;
var data = {name: 'test'};
var createController;
beforeEach(module('plunker'));
beforeEach(inject(function($rootScope, $controller,_$q_) {
$scope = $rootScope.$new();
$q = _$q_;
mockfoo = {
fn3: function(){ }
};
deferred = $q.defer();
spyOn(mockfoo,'fn3').and.returnValue(deferred.promise);
createController = function() {
return $controller('MainCtrl', {
$scope: $scope,
foo: mockfoo
});
};
}));
it('Should call fn3()', function(){
ctrl = createController();
deferred.resolve(data);
$scope.$digest();
expect(mockfoo.fn3).toHaveBeenCalled();
expect($scope.temp).toBe(data);
});
});
I am not quiet sure why this spec is failing. I am resolving the promise and then calling $digest which means that $scope.temp should get a value.
I have a plnkr for this here:
http://plnkr.co/edit/uzWqTT
Any suggestions anybody ?
You have to catch the resolved value by accessing the value in the then function:
var resolvedValue;
$scope.temp.then(function(value) { resolvedValue = value; });
expect(resolvedValue).toEqual(data);
See plunker.
Related
In this Angular/Jasmine example I have a controller with a promise and a $timeout.
The test fails because a variable set in the $timeout is undefined. Other variables not set inside the $timeout don't have this problem.
Only this expect fails, the others work:
expect($scope.notes).toBe(notes);
I used $timeout.flush() to wait but it is ignored. Any ideas how to fix this?
PLUNK http://plnkr.co/edit/Jet3KRs7baTIzk8L30JQ
angular.module("mymodule", [])
.service('myHttp', function($http){
this.call = function($q) {
var defer = $q.defer();
$http({url:"gothere"})
.then(function (response) {
defer.resolve(response);
});
return defer.promise;
};
})
.controller('ctl', function($scope,$timeout,myHttp) {
$scope.read = function (id){
var data = {};
data.id = id;
myHttp.call({url:'/getRecord', data:data})
.then(function(response) {
$scope.id = response.id;
$scope.name = response.nm;
$scope.descrip = response.dsc;
$timeout(function(){
$scope.notes = response.nts;
},1000);
});
};
});
describe('Testing a Controller that uses a Promise', function () {
var $scope;
var $q;
var deferred;
beforeEach(module('mymodule'));
beforeEach(inject(function($controller, _$rootScope_, _$q_, $timeout, myHttp) {
$scope = _$rootScope_.$new();
$q = _$q_;
deferred = $q.defer();
spyOn(myHttp, 'call').and.returnValue(deferred.promise);
$controller('ctl', {
$scope: $scope,
$timeout: $timeout,
myHttp: myHttp
});
$scope.read(1)
$timeout.flush(2000);
}));
it('should resolve promise', function () {
var id = 1;
var name = "John";
var descrip = "This is the description";
var notes = "These are the notes";
var obj = {
id: id,
nm: name,
dsc: descrip,
nts: notes
};
deferred.resolve(obj);
$scope.$apply();
expect($scope.id).toBe(id);
expect($scope.name).toBe(name);
expect($scope.descrip).toBe(descrip);
expect($scope.notes).toBe(notes);
});
});
Give a timeout to read the variable :
it("should be proper after timeout", function(){
expect($scope.notes).toBe(notes);
}, 1000)
The answer is: use $timeout.flush() after $scope.apply();
$scope.$apply();
$timeout.flush();
describe('Controller:insightSettingsController', function() {
'use strict';
var insightSettingsCtrl;
var settingsService;
var UtilsService;
var scope;
var updateMethodDeferred;
var httpBackend;
var deferred;
var q;
var spy;
beforeEach(module('ui.router',proname.components.insightSettings.module.name));
beforeEach(function() {
var mockUtilsService = {};
module(function(_$provide_) {
_$provide_.value('UtilsService', mockUtilsService);
});
inject(function($q) {
deferred = $q.defer();
mockUtilsService.getConfigurations = function() {};
spyOn(mockUtilsService, 'getConfigurations')
.and.returnValue(deferred.promise);
});
});
beforeEach(inject(function(
_$controller_, _$httpBackend_, _$rootScope_, _settingsService_,
_UtilsService_, _$q_) {
scope = _$rootScope_.$new();
spy = jasmine.createSpy();
settingsService = _settingsService_;
UtilsService = _UtilsService_;
httpBackend = _$httpBackend_;
insightSettingsCtrl = function() {
return _$controller_(
dbmcm.components.settings.insightSettingsController,
{$scope: scope, UtilsService: UtilsService});
};
}));
describe(
'Check Existing Function , parameters, Initialization of function',
function() {
it('should call UtilsService.getConfigurations() once',
function() {
insightSettingsCtrl();
deferred.resolve();
scope.$digest();
expect(UtilsService.getConfigurations).toHaveBeenCalled();
});
});
});
Hi, I am writing angular test cases for my separate module.Unfortunately, my module above breaks down my test cases ,getting error debug.js:44 Uncaught Type Error: Cannot read property 'get Configurations' of undefined. I am firing getConfigurations function at the time of initialization of controller please have a look below
constructor: function(UtilsService) {
UtilsService.getConfigurations().then(function(response) {
this.utilsConfig_ = response;
}.bind(this));
}
I think Jasmine's spy only checks if the function has been called, without firing the actual implementation. That's why getConfigurations().then throws an error.
for this I applied .and.callThrough() in my test cases
it('should call UtilsService.getConfigurations() once',
function() {
spyOn(UtilsService, 'getConfigurations').and.callThrough();
insightSettingsCtrl();
deferred.resolve();
scope.$digest();
expect(UtilsService.getConfigurations).toHaveBeenCalled();
});
});
After that I am getting debug.js:44 Uncaught Error: getConfigurations has already been spied upon
Please correct me where I am doing wrong.
Thanks in advance
Either you mock it or inject it if possible.
You are doing both for UtilsService.
I am assuming you injecting UtilsService.
So,remove the following code :
module(function(_$provide_) {
_$provide_.value('UtilsService', mockUtilsService);
});
inject(function($q) {
deferred = $q.defer();
mockUtilsService.getConfigurations = function() {};
spyOn(mockUtilsService, 'getConfigurations')
.and.returnValue(deferred.promise);
})
I am trying to mock a method in a service that returns a promise. The controller:
app.controller('MainCtrl', function($scope, foo) {
var self = this;
this.bar = "";
this.foobar = function() {
console.log('Calling the service.');
foo.fn().then(function(data) {
console.log('Received data.');
self.bar = data;
});
}
this.foobar();
});
The spec file:
angular.module('mock.foo', []).service('foo', function($q) {
var self = this;
this.fn = function() {
console.log('Fake service.')
var defer = $q.defer();
defer.resolve('Foo');
return defer.promise;
};
});
describe('controller: MainCtrl', function() {
var ctrl, foo, $scope;
beforeEach(module('app'));
// inject the mock service
beforeEach(module('mock.foo'));
beforeEach(inject(function($rootScope, $controller, _foo_) {
foo = _foo_;
$scope = $rootScope.$new();
ctrl = $controller('MainCtrl', {$scope: $scope , foo: foo });
}));
it('Should call foo fn', function() {
expect($scope.bar).toBe('Foo');
});
});
When debugging, I can see in the controller the promise object state being 1 (resolved). Yet, the success callback within then is never invoked.
The following Plunker http://plnkr.co/edit/xpiPKPdjhiaI8KEU1T5V reproduces the scenario. Any help would be greatly appreciated.
You must get a reference to $rootScope in your test and call:
$rootScope.$digest()
Your plunk, revisited:
$digest called and test passed.
Also your mock didn't return anything in the resolve, I added:
defer.resolve('Foo');
HTH
I have a promise in a controller that I'm trying to test and I'm getting Error: Unexpected request: POST /v1/users.
I'm trying to spyOn the AuthService.changePassword which returns a promise and test whether it got called or not. Not sure why it's actually making the POST call...
controller
angular.module('example')
.controller('ChangePasswordCtrl', ['AuthService', '$state',
function(AuthService, $state) {
var vm = this;
vm.submitted = false;
vm.submit = function(valid) {
vm.submitted = true;
if (!valid) return false;
AuthService.changePassword(vm.email)
.then(function(res) {
$state.go('reset.confirmation');
}, function(err) {
vm.hasError = true;
});
};
}
]);
unit test
describe('ChangePasswordCtrl', function() {
var ctrl, scope, AuthService, $q, $state, deferred;
beforeEach(module('example'));
function _inject() {
inject(function($controller, $rootScope, _AuthService_, _$state_, _$q_) {
scope = $rootScope.$new();
$state = _$state_;
$q = _$q_;
AuthService = _AuthService_;
ctrl = $controller('ChangePasswordCtrl', {
$scope: scope
});
});
}
describe('#submit', function() {
beforeEach(function() {
_inject();
deferred = $q.defer();
spyOn(AuthService, 'changePassword').and.returnValue(deferred.promise);
spyOn($state, 'go');
});
describe('when email address is valid', function() {
it('should call the changePassword method on the AuthService', function() {
ctrl.submit(true);
scope.$digest();
expect(ctrl.submitted).toBe(true);
expect(AuthService.changePassword).toHaveBeenCalled();
});
});
});
});
Your spec code works for me (the real implementation of AuthService.changePassword doesn't get called): http://jsfiddle.net/7W2XB/7/
angular.module('example', [])
.factory('AuthService', function() {
return {
changePassword: function() {
throw new Error('Should not be called');
}
};
})
.controller('ChangePasswordCtrl', ['AuthService',
function(AuthService) {
var vm = this;
vm.submitted = false;
vm.submit = function(valid) {
vm.submitted = true;
if (!valid) return false;
AuthService.changePassword(vm.email)
.then(function(res) {
$state.go('reset.confirmation');
}, function(err) {
vm.hasError = true;
});
};
}
]);
describe('ChangePasswordCtrl', function() {
var ctrl, scope, AuthService, $q, deferred;
function _inject() {
module('ui.router');
module('example');
inject(function($controller, $rootScope, _AuthService_, _$state_, _$q_) {
scope = $rootScope.$new();
$state = _$state_;
$q = _$q_;
AuthService = _AuthService_;
ctrl = $controller('ChangePasswordCtrl', {
$scope: scope
});
});
}
describe('#submit', function() {
beforeEach(function() {
_inject();
deferred = $q.defer();
spyOn(AuthService, 'changePassword').and.returnValue(deferred.promise);
});
describe('when email address is valid', function() {
it('should call the changePassword method on the AuthService', function() {
ctrl.submit(true);
scope.$digest();
expect(ctrl.submitted).toBe(true);
expect(AuthService.changePassword).toHaveBeenCalled();
});
});
});
});
Some questions that might help make the JSFiddle more realistic to your situation: What versions of angular and Jasmine are you using? - How are you defining the AuthService (presumably using angular.factory)?
My controller does something like below (pls note i had not shown all dependencies for sake of simplicity):
.controller('demo',function(){
fn1(){
var defereed = $q.defer;
/* fetch data from server and once the data is fetched perform a resolve */
return deferred.promise;
}
fn2(){
var defereed = $q.defer;
/* fetch data from server and once the data is fetched perform a resolve*/
return deferred.promise;
}
fun3() { /*makes some server side calls and updates the view*/}
$q.all([fn1(),fn2()] .then(function(result){
if (result[0]) { fn3();}
});
}
Under such circumstances I have having a hard time testing whether fn3() was called. I am also not able to get fn1() and fn2() to resolve so that i can test fn3()
Any tips will be appreciated.
Finally figuered a solution .. For those interested check this plnkr:
http://plnkr.co/HkaQdH
describe('Testing: MainCtrl', function() {
var $scope = null;
var ctrl = null;
var mockInitService;
var defer1,defer2;
var $q;
beforeEach(module('plunker'));
beforeEach(inject(function($rootScope, $controller,$q) {
$scope = $rootScope.$new();
$q = $q;
mockInitService = {
fn1: function(){},
fn2: function(){},
fn3: function(){}
};
defer1 = $q.defer();
defer2 = $q.defer();
spyOn(mockInitService,'fn1').and.returnValue(defer1.promise);
spyOn(mockInitService,'fn2').and.returnValue(defer2.promise);
spyOn(mockInitService,'fn3');
createController = function() {
return $controller('MainCtrl', {
$scope: $scope,
InitService: mockInitService
});
}
}));
it('Should call InitService.fn3', function() {
ctrl = createController();
defer1.resolve();
defer2.resolve();
$scope.$digest();
expect(mockInitService.fn3).toHaveBeenCalled();
});
});