Hello.
I'm getting acquainted with Jasmine and became a bit baffled by the way the spies work.
I'm testing a controller and I've created a spy on a service that it uses.
beforeEach(module(function ($provide) {
$provide.service('myService', MyService);
}));
And in one of the beforeEach blocks, I create the spies on each of the service's methods.
beforeEach(inject(function (_$injector_, _$controller_, _$rootScope_) {
spyOn(myService, 'getAll').and.returnValue([{id: "2", type: "0", name: "Foo"}]);
spyOn(myService, 'getOne').and.returnValue({id: "3", type: "1", name: "Bar"});
spyOn(myService, 'save').and.returnValue(true);
}));
And in the tests I use the spy to investigate the calls to it.
it("should call service.getOne with the id from the routeParams", function () {
routeParams = {id: "2"}
createController();
expect(service.getOne).toHaveBeenCalledWith("2");
});
This all works perfectly fine. So what's the problem?
Well, I assumed that since we're using a spy and not the .and.callThrough(), the actual implementation would not get called. However, when I inserted a console.log() into the actual service implementation, the log message showed up in the terminal when running the tests with karma-jasmine. This indicates that the actual implementation in fact is called although we're using a spy.
What I don't understand is why this is the case. Why would we want pass the call on to the actual implementation if we're explicitly creating a mock to hide it. And why is there an .and.callThrough if all calls are passed through anyway? What if the service uses $http or accesses a database? Am I not using the spy to prevent the actual implementation from being called?
So the only way not to have my tests call the actual implementation is to create a separate mock object with identically named mock functions, inject that into my model in place of the real service and put spies on that object instead.