Reputation: 2740
I have following angular service.
(function () {
"use strict";
angular.module("theModule")
.factory("theService", theServiceFactory);
function theServiceFactory() {
return {
theFn: theFn,
theFailureFn: theFailureFn,
theSuccessFn: theSuccessFn
};
function theFn() {
angular.noop();
}
function theFailureFn() {
theFn();
}
function theSuccessFn() {
this.theFn();
}
}
}());
Functions are defined separately and their references are assigned to object being returned by the factory.
And I have following jasmine test cases.
describe("The Service Test Specifications", function () {
var theService;
beforeEach(module('theModule'));
beforeEach(inject(function(_theService_) {
theService = _theService_;
}));
it("should call 'theFn' from 'theFailureFn'", function () {
spyOn(theService, "theFn");
theService.theFailureFn();
expect(theService.theFn).toHaveBeenCalled();
});
it("should call 'theFn' from 'theSuccessFn'", function () {
spyOn(theService, "theFn");
theService.theSuccessFn();
expect(theService.theFn).toHaveBeenCalled();
});
});
Test Case should call 'theFn' from 'theFailure'
is being failed whereas should call 'theFn' from 'theSuccess'
is being passed.
From source code it seems object's theFn
is referring to function theFn
but actually it's not. It is causing first test case to fail. (Q1) At what stage different reference is assigned to object's theFn
?
Second test case is passing as theFn
is invoked with this
inside theSuccess
. But using this
in that situation is strict mode violation. I like and follow John Papa's style guide so I created factory
and defined all functions below it. (Q2) What would be the better way to write such function?
(Q3) If theFailure
is written correctly, is there any way to detect in test case that function theFn
is called?
Plunkr: http://plnkr.co/edit/PsQTZQlIgWI3OTMaYi7n
Upvotes: 1
Views: 120
Reputation: 1726
Dont use this
in .factory()
. Factory is just executed as function and it just returns what you explicitly specify as return object. Angular uses .service()
as a constructor function which is executed with new
operator and thats the place where you will use this
.
That means that your theFailureFn()
is written correctly.
As for Q3, it may fail just because how spyOn is implemented.
Edit:
As i supposed, its the implementation of spyOn()
.
spyOn wraps the function but in your factory you are still referencing the original function. Try to use expect(this.theFn).toHaveBeenCalled();
Upvotes: 1
Reputation: 27738
It is actually calling theFn()
from theFailureFn()
, and NOT calling theFn()
from the sucesssFn()
. It's the opposite result of your test.
As you see from this,
http://plnkr.co/edit/Tp5FtsL8DAjkcPO0m0ZV?p=preview
console.log('theService.theFn BEFORE spyOn', theService.theFn);
spyOn(theService, "theFn");
console.log('theService.theFn AFTER spyOn', theService.theFn);
Result
theService.theFn BEFORE spyOn theFn()
theService.theFn AFTER spyOn j$.createSpy.spy()
your Jasmine spyOn(theService, "theFn");
is setting an instance of theService, this.theFn
, and your test is checking this.theFn
is called or not. Remember that theService
is a Hash, not a function.
As you see from the output of this line;
console.log('caller theFailureFn', this, theFn, this.theFn)
//OUTPUT
caller theFailureFn Object {} theFn() j$.createSpy.spy()
theFn
and this.theFn
is very different. theFn
is an object property value and this.theFn
is an instance of object, Hash.
To answer your question,
(Q1) At what stage different reference is assigned to object's theFn
theFn
is assigned as you expected. spyOn
makkes difference in your case.
(Q2) What would be the better way to write such function?
To test a function calls a function of an object, it should return a function, not a hash.
(Q3) If theFailure is written correctly, is there any way to detect in test case that function theFn is called? The same answer as Q2.
Upvotes: 3