Reputation: 8959
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 );
Upvotes: 0
Views: 672
Reputation: 3201
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.
Upvotes: 1