1

I have created an Angular factory that has methods which handle saving code to a server. One of the factory methods contains a third party object which has a method which does the actual callout. I would like to test this code, but I can't work out how to mock out the third party object.

I have set up a plunker with a Jasmine test.

My aim for this test is just to successfully get the code to use my mock object rather than the ThirdPartySavingUtils object. Is that possible?

var app = angular.module("MyApp", []);

app.factory("SavingUtils", function() {

  return {

    saveStuff: function() {
      if(typeof ThirdPartySavingUtils !== "undefined") {
        return ThirdPartySavingUtils.value;
      }
    }
  };
});

this is my jasmine tests

describe("Mocking Test", function() {

  var ThirdPartySavingUtilsMock;
  var SavingUtils;

  beforeEach(function() {

    angular.mock.module("MyApp", function($provide) {

      ThirdPartySavingUtilsMock = {
        value: "I am the mock object"
      };

      $provide.value("ThirdPartySavingUtils", ThirdPartySavingUtilsMock);
    });

    inject(function(_SavingUtils_) {
      SavingUtils = _SavingUtils_;
    });
  });

  it("should run without throwing an exception", function() {

    expect(true).toBe(true);
  });

  it("should mock out ThirdPartySavingUtils with ThirdPartySavingUtilsMock", function() {

    var result = SavingUtils.saveStuff();

    expect(result).toEqual("I am the mock object");
  });
});

1 Answer 1

1

You have a few options really but more than likely you would need to do both.

1) You could create an angular service which wraps this third party object - this way you get a nice abstraction incase you ever need to change the third party object.

2) You could use a mocking framework like http://sinonjs.org/ which enable you to mock methods out and do asserts like calledOnce etc.

Here is a link to a mocked test using sinon test.

You can bascially see sinon is used as a sandbox to mock out an object methods. Sinon provides extra propeties to those mocked methods so you can assert if they were called, the parameters they were called with even the order of the calls. It is a really, really essential testing tool.

describe('validationManager', function () {
        beforeEach(inject(function ($injector) {
            sandbox = sinon.sandbox.create();
            $rootScope = $injector.get('$rootScope');
            $compile = $injector.get('$compile');
            $q = $injector.get('$q');
            defer = $q.defer();
            validator = $injector.get('validator');
            validationManager = $injector.get('validationManager');

            sandbox.stub(validator, 'makeValid');
            sandbox.stub(validator, 'makeInvalid');
            sandbox.stub(validator, 'getErrorMessage').returns(defer.promise);

            setModelCtrl();
        }));

        afterEach(function () {
            sandbox.restore();
            setModelCtrl();
        });

        it('should be defined', function () {
            expect(validationManager).to.exist;
        });

        describe('validateElement', function () {
            it('should return if no $parsers or $formatters on the controller', function () {
                validationManager.validateElement(modelCtrl);
                expect(validator.makeValid.called).to.equal(false);
                expect(validator.makeInvalid.called).to.equal(false);
            });

});

EDIT -----------------------

Here this put into practice for your code (I haven't run this but it give the general idea).

(function (angular, ThirdPartyApi) {
    'use strict';

    var app = angular.module('MyApp', []);

    app.factory('thirdPartApi', [
        function () {
            return {
                save: ThirdPartyApi.save,
                value: ThirdPartyApi.value
            };
        }
    ]);

    app.factory('SavingUtils', [
        'thirdPartApi',
        function (thirdPartApi) {
            var getValue = function () {
                    return thirdPartApi.value;
                },
                save = function (item) {
                    return thirdPartApi.save(item);
                };

            return {
                save: save,
                getValue: getValue
            };
        }
    ]);
}(angular, window.ThirdPartyApi));

The tests.....

(function (angular, sinon) {
    'use strict';

    describe('MyApp.SavingUtils', function () {
       var sandbox, thirdPartyApi, SavingUtils, thirdPartyApiValue = 2;

        beforeEach(inject(function ($injector) {
            sandbox = sinon.sandbox.create();
            thirdPartyApi = $injector.get('thirdPartyApi');
            SavingUtils = $injector.get('SavingUtils');

            // stub the method and when called return a simple object or whatever you want
            sandbox.stub(thirdPartyApi, 'save').returns({ id: 1});
            sandbox.stub(thirdPartyApi, 'value', function () {
                return thirdPartyApiValue;
            });
        }));

        afterEach(function () {
            // This removes those stubs and replace the original methods/values
            sandbox.restore();
        });

        describe('save', function () {
            it('should return call the save method on thirdPartyApi', function () {
                var item = {};
                SavingUtils.save(item);

                expect(thirdPartyApi.save.calledOnce).to.equal(true);
            });
        });

        describe('getValue', function () {
            it('should return value of value property on thirdPartyApi', function () {
                var result = SavingUtils.getValue();

                expect(result).to.equal(thirdPartyApiValue);
            });
        });
    });
}(angular, sinon));
Sign up to request clarification or add additional context in comments.

2 Comments

Thanks for your answer and example code. I will have to have a look at Sinon and have a go at wrapping the third party code with a service. Would you be able to tell me what was wrong with the way I was trying to do it? Is it possible to mock that object the way I am trying to do it? Thanks
@Joe see the edit - really glad to see you're thinking about testing!

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.