How to call function after $ionicPlatform.ready in karma jasmine - javascript

Can someone explain how do I expect a call after an $ionicPlatform.ready in my jasmine tests ?
Here a simple sample of code of my controller:
function myController($ionicPlatform, myService) {
$ionicPlatform.ready(function() {
myService
.get()
.then(function() {
// success
})
.catch(function() {
// error
});
});
}
Here is the test :
function myControllerTest() {
var $controller,
$ionicPlatform,
$q,
$rootScope,
controller,
deferred,
myServiceMock;
beforeEach(module('my_module'));
beforeEach(inject(function(_$controller_, _$ionicPlatform_, _$q_, _$rootScope_) {
$controller = _$controller_;
$ionicPlatform = _$ionicPlatform_;
$q = _$q_;
$rootScope = _$rootScope_;
deffered = $q.defer();
myServiceMock = {
'get': jasmine.createSpy('myService.get').and.returnValue(deferred.promise)
};
spyOn($ionicPlatform, 'ready').and.callThrough();
controller = $controller('myController', {
'$ionicPlatform': $ionicPlatform,
'myService': myServiceMock
});
}));
it('should call myService.get after $ionicPlatform.ready is fired', function() {
expect($ionicPlatform.ready).toHaveBeenCalled();
// ---> [OK]
expect(myServiceMock.get).toHaveBeenCalled();
// ---> [ERROR] Expected spy myService.get to have been called
});
}
I hope I have been clear, and someone will be able to help me. I can't find any good documentation on this.
Best regards.

Related

Jasmine spyOn not working properly on AngularJS directive

I'm working on an AngularJS app and I'm facing some problems with Jasmine's SpyOn in a concrete directive.
The directive is quite simple, just call a service's method and when it resolves/rejects the promise acts in consequence, setting some values or another ones.
The problem: When I try to mock SignatureService.getSignatureData SpyOn does not work as I expect, and acts as if I was invoking jasmine's callThrough method over getSignatureData.
I've been using spyOn and mocks in other directives and services, and there was no problem with those.
I've been trying to solve this issue the last two days, comparing with other solutions and user's answers, but I can not find a valid solution.
Here's my code:
AngularJS directive code:
angular
.module('module_name')
.directive('signatureDirective', signatureDirective);
angular
.module('GenomcareApp_signature')
.controller('signatureDController', signatureDController);
function signatureDirective() {
return {
restrict: 'E',
templateUrl: 'components/signature/signature.directive.html',
controller: signatureDController,
controllerAs: 'ctrl',
bindToController: true
};
}
signatureDController.$inject = [
'$scope',
'$rootScope',
'$location',
'SignatureService'
];
function signatureDController($scope, $rootScope, $location, SignatureService) {
var controller = this;
$scope.$on('pdfFileLoadSuccessfully', function (data) {
console.log(data);
controller.loadPdfSucceed = true;
});
$scope.$on('pdfFileLoadFails', function (data) {
console.error(data);
controller.loadPdfError = true;
});
function loadDirectiveInitData() {
var queryParameters = atob($location.search().data);
controller.email = queryParameters.split(';')[0];
controller.phone = queryParameters.split(';')[1];
controller.docid = queryParameters.split(';')[2];
SignatureService.getSignatureData(controller.email, controller.phone, controller.docid)
.then(
function (data) {
console.log(data);
controller.stampTime = data.stamp_time;
controller.fileUrl = data.original_file.url;
},
function (error) {
console.error(error);
controller.error = true
})
.finally(
function () {
controller.endLoad = true;
})
}
loadDirectiveInitData();
}
Jasmine test code:
'use strict';
/* global loadJSONFixtures */
describe('Test :: Signature directive', function () {
beforeEach(angular.mock.module('app'));
beforeEach(module('translateNoop'));
var $q, $compile, $rootScope, controller, $scope, $httpBackend, $location, SignatureService;
beforeEach(angular.mock.inject(function (_$controller_, _$q_, _$rootScope_, _$location_, _$compile_, _$httpBackend_, _SignatureService_) {
$q = _$q_;
$compile = _$compile_;
$location = _$location_;
$scope = _$rootScope_.$new();
$httpBackend = _$httpBackend_;
SignatureService = _SignatureService_;
spyOn($location, 'search').and.returnValue({data: 'dGVzdEB0ZXN0LmNvbTsrMzQ2NjY2NjY2NjY7WG9TUFFnSkltTWF2'});
$httpBackend.whenGET('components/signature/signature.directive.html').respond(200, '');
controller = _$controller_('signatureDController', {$scope: $scope});
}));
describe('Testing directive', function () {
it('Init data should be set when promise resolves/rejects', function (done) {
// SpyOn DOES NOT MOCK THE SERVICE METHOD
spyOn(SignatureService, 'getSignatureData').and.callFake(function () {
return $q.resolve({...})
});
var element = angular.element('<signature-directive></signature-directive>');
element = $compile(element)($scope);
$scope.$digest();
done();
// ... some expect stuff
});
});
});
If any one can give me some advice or solution, I would be very thankful.
Thank you very much.
UPDATE1: I don't know why, but if I do not declare the controller variable in the global beforeEach, Jasmine's spyOn mocks the method as I expect.
Now the issue is how to get the controller to test that the controller values are set as expected.
Well... I realized that the problem was that the controller was being created before all, and somehow when the service was mocked the controller ignores it.
This idea came by accident, when I paste the service's spyOn in the global beforeEach.
So I decide to create a new instance of the controller and the corresponding spyOn with the desired result inside the beforeEach of each describe.
It works. Maybe it's not the best aproach, and I encourage to anyone who have the answer to post it. I'm going to be eternally greatful.
Here's my final test code:
describe('Test :: Signature directive', function () {
beforeEach(angular.mock.module('app'));
beforeEach(module('translateNoop'));
var $q, $compile, $rootScope, $scope, $httpBackend, $location, SignatureService, test_fixture;
beforeEach(angular.mock.inject(function (_$q_, _$rootScope_, _$location_, _$compile_, _$httpBackend_, _SignatureService_) {
$q = _$q_;
$compile = _$compile_;
$location = _$location_;
$scope = _$rootScope_.$new();
$httpBackend = _$httpBackend_;
SignatureService = _SignatureService_;
// controller = _$controller_;
spyOn($location, 'search').and.returnValue({data: 'dGVzdEB0ZXN0LmNvbTsrMzQ2NjY2NjY2NjY7WG9TUFFnSkltTWF2'});
$httpBackend.whenGET('components/signature/signature.directive.html').respond(200, '');
}));
describe('Testing directive when service resolve promise', function () {
var controller;
beforeEach(inject(function(_$controller_) {
spyOn(SignatureService, 'getSignatureData').and.callFake(function () {
return $q.resolve({...})
});
controller = _$controller_('signatureDController', {$scope: $scope})
}));
it('Init data should be set', function () {
// spyOn($location, 'search').and.callThrough();
var element = angular.element('<signature-directive></signature-directive>');
element = $compile(element)($scope);
$scope.$digest();
// ... some expect(...).toEqual(...) stuff and more
});
});
});
Thank you for your time.
Try to use $q.defer(), here's an example:
it('Init data should be set when promise resolves/rejects', function (done) {
// SpyOn DOES NOT MOCK THE SERVICE METHOD
spyOn(SignatureService, 'getSignatureData').and.callFake(function () {
let deferred = $q.defer();
deferred.resolve({...});
return deferred.promise;
});
var element = angular.element('<signature-directive></signature-directive>');
element = $compile(element)($scope);
$scope.$digest();
done();
// ... some expect stuff
});

Controller is undefined in jasmine on correct module and controller

The variable homecontroller in my spec is undefined, even though i am using a correct module and correct controller. my code is
describe('HomeController Tests', function () {
//var $translateProvider;
beforeEach(module('myModule'), function ($provide, $translateProvider) {
$provide.factory('customLoader', function ($q) {
return function () {
var deferred = $q.defer();
deferred.resolve({});
return deferred.promise;
};
});
$translateProvider.useLoader('customLoader');
});
var homecontroller, scope;
beforeEach(inject(function ($rootScope, $controller, $httpBackend, SessionStorageService) {
SessionStorageService.Set("LoginResponse", { SessionID: 'h5' });
scope = $rootScope.$new();
homecontroller = $controller('HomeController',
{
$scope: scope
});
$httpBackend.when('GET', 'Translations/locale-en.json?lang=en').respond(200);
}));
similar to this way i wrote a spec file for another controller for this same module, but it works fine. That spec controller code is
describe('LoginController Tests', function () {
var $translateProvider;
beforeEach(module('myModule'), function ($provide, $translateProvider) {
$provide.factory('customLoader', function ($q) {
return function () {
var deferred = $q.defer();
deferred.resolve({});
return deferred.promise;
};
});
$translateProvider.useLoader('customLoader');
});
beforeEach(inject(function ($httpBackend, $controller, SessionStorageService, $injector, $window, Route, _$translateStaticFilesLoader_, $translate, HttpRequestSoapAPI) {
RouteMock = $injector.get("Route");
SessionStorageService.Set("LoginResponse", {SessionID : 'h5'});
logincontroller = $controller('LoginController',
{
//some services here
});
$httpBackend.when('GET', 'Translations/locale-en.json?lang=en').respond(200);
}));
I got this logincontroller beautifully, but i didnt get that homecontroller.
Note: Both controller js files available in main page, initially login page with loginController loads, from there it navigates to home page with homeController.
Any thoughts please

AngularFire karma test never reaches promise callback

I have a controller that uses AngularFire's $firebaseObject.
When the controller initializes, it sets the reference to Firebase, and uses $loaded function to verify that data from Firebase is actually there. Here is a simplified code of the controller:
myModule.controller('VotingQuestionsCtrl', ['$scope', '$rootScope', '$firebaseObject',
function($scope, $rootScope, $firebaseObject) {
var baseRef = new Firebase("https://somefirebaserepo.firebaseio.com/pathtomyobjects"),
topicsRef = baseRef.child("topics");
var topics = $firebaseObject(topicsRef);
$scope.dataLoaded = false;
topics.$loaded().then(function() {
$scope.dataLoaded = true;
topics.$bindTo($scope, 'topics').then(function() {
//do some stuff
});
}, function(error) {
$scope.dataLoaded = true;
console.log("oh no!");
});
This controller works pretty good in real life. The problem is testing it with Karma. No matter what I tried, my test spec never reaches the callback for topics.$loaded (neither the success function nor the error function).
Here is the current state of my test spec:
describe('VotingQuestionsCtrl', function() {
var $controller, $scope, $rootScope, $firebaseObject, $stateParams, $q, baseRef, $timeout;
beforeEach(module('wsApp.controllers'));
beforeEach(inject(function ($injector) {
MockFirebase.override();
$controller = $injector.get('$controller');
$q = $injector.get('$q');
$rootScope = $injector.get('$rootScope');
$timeout = $injector.get('$timeout');
$scope = $rootScope.$new();
$firebaseObject = $injector.get('$firebaseObject');
$rootScope.user = {username: "user"};
$rootScope.selectedSim = {phase: "phase1"};
baseRef = new Firebase("https://somefirebaserepo.firebaseio.com/pathtomyobjects");
baseRef.set({topics: {simId: {phase1: "bla"}}, users: {}});
baseRef.flush();
}));
it('loads the controller', function(done) {
$controller('VotingQuestionsCtrl', {$scope: $scope, $rootScope: $rootScope, $firebaseObject: $firebaseObject});
$scope.$digest();
setTimeout(function() {
expect($scope.dataLoaded).toBeTruthy();
done();
}, 200);
});
});
I've tried all sorts of flushing, digesting, whatever... the test goes through the rest of the controller, but the async part just doesn't work.

TypeError: undefined is not a function Angularjs

I've a unit tests that's keep failing with the error above not sure what exactly is wrong with this, by the looks of the error, its says that I need to declare a function. Can someone point me what is wrong with my code please:
unit test
describe('Test loginService', function() {
var $location, loginService, $scope, authentication, $rootScope;
beforeEach(function() {
module('app');
inject(function(_$location_, _$rootScope_, _loginService_,
_authentication_) {
$location = _$location_;
authentication = _authentication_;
loginService = _loginService_;
$rootScope = _$rootScope_;
$scope = $rootScope.$new();
});
});
it('Should successfully login with correct credentials', function() {
$scope.username = 'a#a.com';
$scope.password = 'a';
spyOn($location, 'url');
loginService.login($scope);//Complains about this line
expect($location.url).toHaveBeenCalled();
expect($location.url).toHaveBeenCalledWith('/home');
});
login service
app.factory('loginService', function(parserService, $location,
authentication, $rootScope) {
return {
login : function(scope) {
parserService.getData().then(function(data) { //This line
if (scope.username === data.username
&& scope.password === data.password) {
...
$location.url("/home");
} else {
scope.loginError = "Invalid Credentials";
}
});
}
});
Json file reader service
app.factory('parserService', function($http, $q) {
return {
getData: function() {
var deferred = $q.defer();
$http.get('res/file.json').success(function(data) {
deferred.resolve(data);
}).error(function(){
deferred.reject();
});
return deferred.promise;
}
}
});
error log
Chrome 36.0.1985 (Windows 7) Test loginService Should successfully login with correct credentials FAILED
TypeError: undefined is not a function
at Object.login (D:../js/services/loginService.js:6:18)
at null.<anonymous> (D:/../test/unit/loginTest.js:92:16)
since you can't format a comment, I'm using an answer... :D
have you tried this?
var $location, loginService, $scope, authentication, $rootScope, parserService;
beforeEach(function() {
module('app');
inject(function(_$location_,
_$rootScope_,
_parserService_,
_loginService_,
_authentication_) {
$location = _$location_;
authentication = _authentication_;
loginService = _loginService_;
$rootScope = _$rootScope_;
$scope = $rootScope.$new();
parserService = _parserService_;
});
});

Jasmine unit test $scope access

Why am I unable to access my function in my Controller? The code functions like I would expect it too, however, it doesn't seem to want to allow me access to my function that I'm trying to unit test. It should just return a simple bool, but it's getting killed somewhere.
Here's some code:
RTHelper.js
describe('Unit: LocationController', function () {
var $scope, $httpBackend, $location, injector, ctrl, $controller;
//beforeEach(function () {
// angular.module('TDE').controller('LocationController'); //
// inject(function ($injector) {
// $rootScope = $injector.get('$rootScope');
// $scope = $rootScope.$new();
// //ctrl = $injector.get('$controller')("LocationController", { $scope: $scope });
// injector = $injector;
// ctrl = $injector.get('$controller');
// //scope = $injector.get('$rootScope').$new();
// $httpBackend = $injector.get('$httpBackend');
// $location = $injector.get('$location');
// });
//});
//both beforeEach methods work(which one is better? I don't know), so things are getting loaded
beforeEach(function () {
angular.module('TDE');
inject(function ($injector) {
$location = $injector.get('$location');
$rootscope = $injector.get('$rootScope');
$scope = $rootscope.$new();
$controller = $injector.get('$controller');
ctrl = function () {
return $controller('LocationController', {
'$scope': $scope
})
};
})
});
it("should just be a holder for something for later", function () {
expect($scope.BoolCondition()).toBeDefined(); //I don't care what it returns as long as it's accessed honestly
});
})
LocationController.js
angular
.module('TDE')
.controller('LocationController', ['$rootScope', '$scope', '$location', '$window', '$document', 'LocationService', 'HeaderFooterService', 'SearchService', 'TranslationService', 'MTDE_CONFIG', 'LocationPartnerAssignmentService', 'ExperimentService', function ($rootScope, $scope, $location, $window, $document, $LocationService, $HeaderFooterService, $SearchService, $TranslationService, $MTDE_CONFIG, $LocationPartnerAssignmentService, $ExperimentService) {
$scope.BoolCondition = function(myCondition){
if(//blah blah condition test on myCondition)
{
return true
}
else
{
return false;
}
}
How would I go about getting to that BoolCondition? I'm new to this so you can imagine the struggle of writing unit tests after never having done unit testing. I've also gone through countless examples and I've done some generic tests, so I'm not totally un-versed.
You're not bootstrapping the module under test correctly. You should use angular.mock.module inside the test
You're not instantiating the controller (where's the call to ctrl()?)
Here's the complete working example and the fiddle that runs it:
describe('Unit: LocationController', function () {
var $scope, $location, ctrl;
beforeEach(function () {
angular.mock.module('TDE');
inject(function (_$location_, _$rootScope_, _$controller_) {
$location = _$location_;
$scope = _$rootScope_.$new();
ctrl = _$controller_('LocationController', {
'$scope': $scope
})
});
});
it("should just be a holder for something for later", function () {
expect($scope.BoolCondition()).toBeDefined();
expect($scope.BoolCondition()).toBeTruthy();
});
})

Categories