How to mock angular.element.injector().get; - angularjs

I have controller code like this in an angularjs file...
function sendAnalytics(searchParams) {
var analyticServiceCall = angular.element(document.body).injector().get('SomeElement');
};
When I run test cases attached with the above controller I get the following error
* TypeError: Cannot call method "get" of undefined in http://localhost:46309/src/soemFolder/someController.js (line XYZ)
What I am understanding is, I need to mock angular.element(document.body).injector().get function but how to do it?

beforeEach(inject(function($injector) {
angular.element.fn.extend({ injector: function() { return $injector } });
});
You can find more information here

Related

Jasmine Js - SpyOn Fakecall during the controller initialization

I have seen a set of duplicates for this question but was unable to solve the issue.
I have a controller and during the controller initialization, fetchtemplate() is getting called first and then my mock fetchtemplate() is getting called.
How do I stop the actual(controller) fetchtemplate() getting called during the controller initialization? My intention is to mock the function fetchtemplate() in my spec.Please have a look at my spec -
describe("...",function(){
beforeEach(inject(function($controller,...) {
scope = $rootScope.$new();
this.init = function() {
$controller('ChangeControlCreateController', {
$scope: scope
});
}
}));
describe('Function', function() {
it("-- check for trueness",function(){
this.init() ; //Initialization of the controller
spyOn(scope,'fetchtemplate').and.callFake(function() {
return 101;
});
var fakeResponse = scope.fetchtemplate();
expect(scope.fetchtemplate).toHaveBeenCalled();
expect(fakeResponse).toEqual(101);
});
});
});
I have tried placing the spyOn before the this.init() which gave error as the fetchtemplate() doesn't exist at that time to spyOn.
My controller code structure looks like -
angular.module('...', [...])
.controller('ChangeControlCreateController', ["$scope"...,
function ChangeControlCreateController($scope,...) {
$scope.fetchtemplate = function() {
console.log("controller's function");
...
};
$scope.fetchtemplate();
});
The result what I am getting is - First the console item "controller's function" and then the spec is executing with mock function. I want the mock function to execute without the controller's function to execute
So if I understand correctly you are doing some call to a function that is doing something you want to prevent for test purposes. Probably an http call or some thing of the sort ?
Whatever it is doing the proper way to handle something like that is usually to put that method inside a service instead and then to spy on that service method. Here is an example of test if the service is TemplateService :
describe("...",function(){
var $controller, scope, TemplateService, YourController;
beforeEach(inject(function(_$controller_, _TemplateService_, ...) {
scope = $rootScope.$new();
$controller = _$controller_;
TemplateService = _TemplateService_;
}
it("-- check for trueness",function(){
spyOn(TemplateService,'fetchTemplate').and.returnValue('101');
YourController = $controller('YourController');
expect(...);
});
});
I hope that's helpful

Unit testing a helper inside a custom provider in angularjs

I have this provider:
angular.module('app').provider('appProvider', function() {
this.$get = Helper;
function Helper() {
function method() {
return true;
};
return {
method: method
};
});
When unit testing it, I can reach appProvider, but not the Helper in unit tests. I've been trying this:
describe('test', function() {
var prov;
beforeEach(angular.mock.module('app', function(appProvider) {
prov = appProvider;
}));
it('provider', inject(function() {
expect(prov.Helper.method()).toEqual(true);
}));
});
And getting this error:
TypeError: 'undefined' is not an object (evaluating 'prov.Helper.method()')
Question is: How do I reach method() in order to evaluate is correct behaviour?
You are trying to test a method of the service that your provider provides, so it seems a bit roundabout to test the provider. Why not just test the service instead?

Mocking a service used in a filter

I'm writing a unit test for a very simple filter:
app.filter('htmlsafe', ['$sce', function($sce) {
return function(message) {
return $sce.trustAsHtml(message);
};
}]);
I want to mock the $sce service and verify that the trustAsHtml method is being called. Checking the docs has not lead me to much success and after much googling this is the best I can come up with (still not working):
(function (describe, it, expect, inject, beforeEach, module) {
describe('htmlsafe filter', function () {
var htmlsafe, $sce, untrustedString;
beforeEach(module('ComstackPmApp'));
beforeEach(function() {
module(function ($provide) {
$provide.service('$sce', $sce);
});
});
beforeEach(inject(function(htmlsafeFilter) {
htmlsafe = htmlsafeFilter;
untrustedString = '<p>Untrusted</p>';
$sce = {
trustAsHtml: function() {
// stub method to spy on.
}
};
}));
it('Should mark a string as HTML safe', function () {
spyOn($sce, 'trustAsHtml');
htmlsafe(untrustedString);
expect($sce.trustAsHtml.calls.count()).toEqual(1);
});
});
})(describe, it, expect, inject, beforeEach, angular.mock.module);
However this leaves me with the following error message:
PhantomJS 1.9.8 (Mac OS X 0.0.0) htmlsafe filter Should mark a string as HTML safe FAILED
TypeError: 'undefined' is not an object (evaluating '(isArray(Type) ? Type[Type.length - 1] : Type).prototype')
undefined
at instantiate (bower_components/angular/angular.js:4480)
at bower_components/angular/angular.js:4341
at invoke (bower_components/angular/angular.js:4473)
at enforcedReturnValue (bower_components/angular/angular.js:4325)
at invoke (bower_components/angular/angular.js:4473)
at bower_components/angular/angular.js:4290
at getService (bower_components/angular/angular.js:4432)
at invoke (bower_components/angular/angular.js:4464)
at enforcedReturnValue (bower_components/angular/angular.js:4325)
at invoke (bower_components/angular/angular.js:4473)
at bower_components/angular/angular.js:4290
at getService (bower_components/angular/angular.js:4432)
at invoke (bower_components/angular/angular.js:4464)
at workFn (bower_components/angular-mocks/angular-mocks.js:2426)
Error: spyOn could not find an object to spy upon for trustAsHtml()
at specs/filters/HtmlSafeFilter.js:26
Not sure what you're trying to do with all that stuff. You don't need to provide the $sce service: Angular provides it. You don't have to create a fake one either: just spy on the angular-provided service:
describe('htmlsafe filter', function() {
var htmlsafe, $sce, untrustedString;
beforeEach(module('ComstackPmApp'));
beforeEach(inject(function(_$sce_, htmlsafeFilter) {
htmlsafe = htmlsafeFilter;
untrustedString = '<p>Untrusted</p>';
$sce = _$sce_;
}));
it('Should mark a string as HTML safe', function () {
spyOn($sce, 'trustAsHtml');
htmlsafe(untrustedString);
expect($sce.trustAsHtml.calls.count()).toEqual(1);
});
});

angularjs app.service(...) causes "Uncaught TypeError: undefined is not a function"

What worked well in former projects causes an error in the current project.
I have a couple of services for various tasks like this:
angular.module('myApp')
.service('test1', function() {
return {
sayHello : function() {
return ("Hello!");
}
};
})
.service('test2', function() { //this line causes the error
return {
sayHi : function() {
return ("Hi!");
}
};
})
.service('test3', function() {
...
})
When I include this file in my project I get the following error message in the line with the ".service('test2', function()...":
Uncaught TypeError: undefined is not a function
Whatever I am doing - the 2nd method always causes this error. Looks like the first service method does not return an object? Thanks!
It looks like you're missing some closing brackets, so the error that seems to be happening in the second service is actually being caused by a syntax error in the first service.
Try this:
angular.module('myApp')
.service('test1', function() {
return {
sayHello : function() {
return ("Hello!");
}
};
})
.service('test2', function() {
return {
sayHi : function() {
return ("Hi!");
}
};
});
I have now found out what's the problem. I am working on an application which was started by someone else (CLIP-TWO Theme), who defined all services as providers in his router configuration file. That goes like this:
app.service = $provide.service;
When I now define my services the first one will work but the second one won't, because the second one is not in app scope but in provider scope.
So I had to change my service definition like this:
var app = angular.module('myApp'); // this is already done in the main js.
// define all services separately
app.service('test1', function() {...});
app.service('test2', function() {...});
app.service('test3', function() {...});
Try to declare service methods with this.
angular.module('myApp')
.service('test1', function() {
this.sayHello = function() {
return 'Hello';
};
});

TypeError on a controller function`

I'm running a single test on my controller to determine if it's properly defined but I keep getting a TypeError: undefined on the controller object. Here's the complete error:
Search Controller
should have the controller defined <<< FAILURE!
* TypeError: 'undefined' is not an object (evaluating 'myMenuDataLoad.then')
* Expected undefined to be defined.
And here is the controller to be tested:
myAppControllers.controller('VisibilitySearchController', ['$scope', 'headerService', 'menuService', 'navigationService', function($scope, headerService, menuService, navigationService ){
headerService.setTitle('My title');
var myMenuDataLoad = menuService.loadData('partials/common/components/menu-bar/json/menu-bar.json');
myMenuDataLoad.then(function(dataResult){
menuService.setData(dataResult.data);
});
var myNavDataLoad = navigationService.loadData('partials/common/components/navigation-bar/json/navigation-bar.json');
myNavDataLoad.then(function(dataResult){
navigationService.setData(dataResult.data);
});
}]);
I've initialized the controller by passing it everything it needs in its parameters i.e. scope, headerService, menuService and navigationService - I mock these services using the jasmine.createSpyObj method and pass in all the relevant methods ( the ones used on the controller ):
// Mock our services
beforeEach(function() {
// Methods are accepted as the 2nd second parameter
headerService = jasmine.createSpyObj('headerService', ['setTitle']);
module(function($provide) {
$provide.value('headerService', headerService);
});
menuService = jasmine.createSpyObj('menuService', ['loadData', 'setData']);
module(function($provide) {
$provide.value('menuService', menuService);
});
navigationService = jasmine.createSpyObj('navigationService', ['loadData', 'setData']);
module(function($provide) {
$provide.value('navigationService', navigationService);
});
});
And the actual initialization of the controller happens here:
beforeEach(inject(function($rootScope, $injector, $controller, _headerService_, _menuService_, _navigationService_) {
scope = $rootScope.$new();
// Instantiate the controller
searchController = $controller('VisibilitySearchController', {
$scope : scope,
headerService : headerService,
menuService : menuService,
navigationService : navigationService
});
}));
So what am I doing wrong here? Why isn't the test (see below) passing?
it("should have the controller defined", function() {
expect(searchController).toBeDefined();
});
Have I mocked the services correctly? What action needs to be done on a local controller variable in order to properly initialize them and the methods they are used in?
Thanks!
UPDATE
I've looked further into this but am unfortunately still receiving the same undefined error. When you create a mock object of a service do you have to provide that service with all of its dependencies and methods you make use of? For example:
menuService = jasmine.createSpyObj('menuService', ['$parse','$q', 'dataService', 'loadData', 'then']);
module(function($provide) {
$provide.value('menuService', menuService);
});
Here when I create the mock object I provide it with all the dependencies it would expect plus I added in two functions that I make use of in the controller.
So how do I go about mocking a function in a mocked object? I tried this but I'm still getting the same error:
menuService.loadData = jasmine.createSpy( 'loadData()' ).andReturn( data );
As mentioned in the comment your menuService.loadData() will always return undefined so evaluating expression myMenuDataLoad.then will always fail as mentioned in the error. What you must do is to provide an implementation of menuService.loadData which will return a promise. You can do the mocking the way you did it in case you want these method to be called but you don't rely on any return value of it. If you need the method to return something you can do define menuService this way:
var menuService = {
loadData: function() {
var deferred = $q.defer();
var data = []; //put any data you need here to be returned within the promise
deferred.resolve{data);
return deferred.promise;
}
}
module(function($provide) {
$provide.value('menuService', menuService);
});
You will need instance of $q which you can get in your inject call similarly to $rootScope, $injector etc.
In case you wanted to spy on menuService.load function you can do it this way:
spyOn(menuService, "loadData").andCallThrough()
That will keep your mocked implementation of the method but still allow you to assert it was called etc. I don't think you need it.

Resources