Reputation: 95
I am trying to unit test a constructor of mine. Within the constructor, I am injecting a custom service that houses data and makes http requests. within my save function of my constructor, I instantiate an object on the scope by calling the constructor to my custom service.
I want to test to make sure that the constructor of my custom service is being called. I am trying to use a jasmine spy to spy on the constructor call, but unsuccessfully. I am trying to follow an example given by Jasmine on how to perform a spy on the constructor ([Jasmine Spies])1, but it is not working.
My controller is defined as follows:
controller('comments.EditCtrl', ['$scope', '$location', '$routeParams', 'Comment', function($scope, $location, $routeParams, Comment) {
$scope.save = function(comment) {
$scope.comment = new Comment(comment);
$scope.comment.postId = $routeParams.postId;
Comment.save($scope.comment, function() {
$location.path('/blog/' + $routeParams.postId);
});
};
}])
The custom service is named 'Comment'. The line of interest is
$scope.comment = new Comment(comment);
I can't get the test for this to work properly. My test code for the controller is as follows:
describe('comment.edit', function() {
beforeEach(function() {
module('app');
module('blog.comments');
module('comments.edit');
});
describe('edit controller', function() {
var $scope, editCtrl, Comment, commentNamespace;
var mockComment = {
email: '[email protected]',
text: 'mock text'
};
beforeEach(inject(function($rootScope, $injector, $controller) {
var routeParamsStub = jasmine.createSpy('routeParamsStub');
routeParamsStub.postId = '7';
Comment = $injector.get('Comment');
commentNamespace = {
Comment: Comment
};
commentNamespace.Comment.save = function(comment, callback) {
callback();
return '';
};
$scope = $rootScope.$new();
editCtrl = $controller('comments.EditCtrl', {
$scope: $scope,
$routeParams: routeParamsStub,
Comment: commentNamespace.Comment
});
}));
it('should have a edit controller', function() {
expect(editCtrl).not.toBe(null);
expect(editCtrl).not.toBe(undefined);
});
describe('save function', function() {
beforeEach(function() {
spyOn(commentNamespace, 'Comment');
$scope.save(mockComment);
});
//TODO: figure out how to spy on Comment constructor call
it('should create a Comment object via its constructor', function() {
expect(commentNamespace.Comment).toHaveBeenCalled();
});
});
});
});
You can see I am trying create a namespace as Jasmine suggests, and then spy on the 'Comment' for it to hopefully pick up the constructor call. When I run the test, I get the following error message from Karma:
[2013-07-23 21:58:47.720] [DEBUG] config - autoWatch set to false, because of singleRun
INFO [karma]: Karma server started at http://localhost:8080/
INFO [launcher]: Starting browser Chrome
INFO [Chrome 28.0 (Mac)]: Connected on socket id QmSDHIAEJnuGEbe8Zmq3
Chrome 28.0 (Mac) LOG: null
Chrome 28.0 (Mac) comment.edit edit controller save function should create a Comment object via its constructor FAILED
Expected spy constructor to have been called.
Error: Expected spy constructor to have been called.
at null.<anonymous> (/Users/spencer/Projects/angular-blog/src/app/blog/comments/edit/edit.unit.js:68:55)
Chrome 28.0 (Mac): Executed 20 of 20 (1 FAILED) (0.266 secs / 0.092 secs)
Warning: Task "karma:unit" failed. Use --force to continue.
Aborted due to warnings.
I feel like I have tried every combination of other tactics like spying on Comment.prototype.constructor, but I get the same message every time. Does anyone have any clue on how to do this? I know there is a lot here, let me know if there are any holes in the info where I may have left something out. Thanks
Upvotes: 4
Views: 3918
Reputation: 2638
This is due to how jasmine installs spies. When you say spyOn(foo, 'bar')
jasmine replaces the bar
attribute on foo
with a spy object that behaves like a method. This means that when you pass off the commentNamespace.Comment
to your controller, it already has a reference to the original constructor, so when you spyOn(commentNamespace, 'Comment')
later, it's too late.
Upvotes: 2