Bilal
Bilal

Reputation: 2740

Factory method reference confusion, and guideline to code it

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

Answers (2)

tomastrajan
tomastrajan

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

allenhwkim
allenhwkim

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

Related Questions