Jasmine unit test $scope access - javascript

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();
});
})

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
});

AngularJS: TypeError: undefined is not an object for localstorage

I want to test the values in localstorage. Here is the package I use to store those values: https://www.npmjs.com/package/ngstorage
Here is my code:
var module = angular.module(‘myModule', ['ngStorage']);
module.controller(‘MyController’, ['$scope', '$localStorage', function($scope,$localStorage){
$scope.storage = $localStorage;
$scope.data = {
name: “55 Cherry St.“
};
$scope.storage.name = $scope.data.name;
}]);
I want to test the above code in Jasmine and Mocha. I dont know how I can as it gives me this error now:
TypeError: undefined is not an object (evaluating 'expect($localStorage).to.be') (line 14)
Here is my test code:
describe('my Module', function () {
var $controller;
var $scope;
var element;
beforeEach(module('myModule'));
beforeEach(module('ngStorage'));
beforeEach(function() {
$scope = {};
inject(function ($controller, $rootScope, $compile, $localStorage) {
var $controller = $controller('myController', {$scope: $scope});
});
});
describe('My controller', function () {
it('should contain a $localStorage service', inject(function(
$localStorage
) {
expect($localStorage).not.to.equal(null);
}));
});
});
Jasmine doesn't have a expect('something').not.to.equal() function. Use this instead:
expect($localStorage).not.toBe(null);
Also, while reproducing your error, myController is not defined. Fixed using:
var $controller = $controller('MyController', {$scope: $scope}); // MyController (uppercase)
And I think beforeEach(module('ngStorage')); is not necessary, since it already is a dependency of your module.
You have to add a variable for it in order to test it.Below is the improvement you need to do in your code
describe('my Module', function () {
var $controller;
var $scope;
var element;
var $localStorage;
beforeEach(module('myModule'));
beforeEach(module('ngStorage'));
beforeEach(function() {
$scope = {};
inject(function ($controller, $rootScope, $compile, $localStorage) {
var $controller = $controller('myController', {$scope: $scope, $localStorage: $localStorage});
});
});
describe('My controller', function () {
it('should contain a $localStorage service', inject(function(
$localStorage
) {
expect($localStorage).not.to.equal(null);
}));
});
});
OR
expect(controller.$localStorage).not.to.equal(null); //$localStorage is part of controller now

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.

Invoking service method in Jasmine

Using Angular and Jasmine I would like to run the service method with some mockup data. Below is the code of my test which uses some working RoomsController trying to run test() method on the RoomsParamsSvc:
describe('Rooms Controller', function() {
var RoomsController,
scope,
location,
httpBackend,
RoomsParamsSvc;
beforeEach(module('rooms', function ($provide, $injector) {
RoomsParamsSvc = function () { //(1a)
return $injector.get('RoomsParamsSvc'); //(1b)
}; //(1c)
$provide.value('RoomsParamsSvc', RoomsParamsSvc); //(1d)
}));
beforeEach(inject(function($controller, $rootScope, $location, $httpBackend, _RoomsParamsSvc_) {
// Set a new global scope
scope = $rootScope.$new();
location = $location;
httpBackend = $httpBackend;
RoomsParamsSvc = _RoomsParamsSvc_;
RoomsController = $controller('RoomsController', {
$scope: scope,
$location: location,
RoomsParamsSvc: RoomsParamsSvc
});
}));
it('should have test as a function', function () {
var t = RoomsParamsSvc.test();
});
});
As far as I understand with the with injector I should be able to use that injected service. Without (1a-1d) I got an error:
Error: [$injector:unpr] Unknown provider: RoomsParamsSvcProvider <-
RoomsParamsSvc
However now it doesn't work, too. I got an error meaning that test() is not a function:
jasmine typeerror 'undefined' is not a function (evaluating 'RoomsParamsSvc.test()')
My service looks like that:
var roomsApp = angular.module('rooms', []);
roomsApp.factory('RoomsParamsSvc', function () {
var factory = {};
factory.test = function ()
{
return '';
}
return factory;
});
Do you have any suggestions?
Lines 1a-1d are not required, as the 'RoomsParamsSvc' is loaded within your 'room' module. But you make a reference to the RoomsController, which is undefined.
beforeEach(module('rooms'));
beforeEach(inject(function($controller, $rootScope, $location, $httpBackend, _RoomsParamsSvc_) {
// Set a new global scope
scope = $rootScope.$new();
location = $location;
httpBackend = $httpBackend;
RoomsParamsSvc = _RoomsParamsSvc_;
RoomsController = $controller(function() {}, {
$scope: scope,
$location: location,
RoomsParamsSvc: RoomsParamsSvc
});
console.log(RoomsParamsSvc);
}));
Plunker

Test angular controllers with Jasmine - module issue

I decided to learn how to test my angular code with Jasmine. Everything works when I don't use specific dependencies but if there are some dependencies I have problems. For example we have controllers.js:
angular.module('myApp', ['jmdobry.angular-cache'])
.controller('MyCtrl', ['$scope', '$http', function($scope, $http) {
$scope.myName = "Wojtek";
/...
}]);
And now I want to test:
describe('myApp', function() {
var scope,
controller;
beforeEach(angular.mock.module('myApp'));
describe('MyCtrl', function() {
beforeEach(inject(function ($rootScope, $controller) {
scope = $rootScope.$new();
controller = $controller('MyCtrl', {
'$scope': scope
});
}));
it('sets proper name', function () {
expect(scope.myName).toBe("Wojtek");
});
});
});
My question is - how to mock that 'jmdobry.angular-cache' dependency?
Since you don't need the actual mocking functionality for that module in your tests you can do something like this:
describe('myApp', function () {
var scope,
controller;
beforeEach(angular.mock.module('jmdobry.angular-cache', [])); // just create a module that does nothing
beforeEach(angular.mock.module('myApp'));
describe('MyCtrl', function () {
beforeEach(inject(function ($rootScope, $controller) {
scope = $rootScope.$new();
controller = $controller('MyCtrl', {
'$scope': scope
});
}));
it('sets proper name', function () {
expect(scope.myName).toBe("Wojtek");
});
});
});

Categories