0

I've got right now a project were we need to have the backend server mocked for the time being and we are using $httpBackend on the application .run feature. I need to unit test this service that contains the $httpBackend as we will be having a vast amount of mocked calls to the server we will be covering. So right now this is what I have. As a preface to my question the current setup works when I call mockDataService.getWorkflowTask from a controller on a simple page.

My Server replacement service:

angular.module('app').run(function ($httpBackend, $resource, FakeBackendService) {
    // TODO: add all necessary http intercepts.  
    $httpBackend.whenGET('JSON file').respond(function (method, url, data) {
        var request = new XMLHttpRequest();
        request.open('GET', url, false);
        request.send(null);
        return [request.status, request.response, {}];
    });
    $httpBackend.whenGET(/.*/).respond(function (method, url, data) {

        return [200, FakeBackendService.getWorkflowTasks(), {}];
    });
});

Here is the service for FakeBackendService:

(function () {
    'use strict';

    var injectParams = [];

    function service(lodash) {
        var vm = this;

        var ret = {
            getWorkflowTasks: getWorkflowTasks
        };

        function getWorkflowTasks() {
            if (vm.workflowtasks.length < 1) {
                vm.workflowtasks = loadWorkflowTasks("Some JSON file");
            }
            return vm.workflowtasks;
        };

        function loadWorkflowTasks(file) {
            var workflowTasks = [];
            var request = new XMLHttpRequest();
            request.open("GET", file, false);
            request.send(null);

            if (request.status == 200) {
                workflowTasks = angular.fromJson(request.response);
            }
            return workflowTasks;
        };

        function init() {
            vm.workflowtasks = [];
        }

        init();

        return ret;
    }

    service.$inject = injectParams;
    angular.module('mock.FakeBackendService', []).service('FakeBackendService', service);
})();

So that is currently the backend server replacement mock. The following is my data handling service which contains the call to $http.get(blah blah blah).

(function () {
    'use strict';

    var injectParams = ['$http', '$q', 'mockConfigService', '$httpBackend'];

    function factory($http, $q, configService, $httpBackend) {
        var vm = this;

        var factory = {
            getWorkflowTask: getWorkflowTask
        };

        function getWorkflowTask(str) {
            return getResource(str);
        }

        function init() {
            // Get the URL we will be using to get data from
            vm.dataServiceURL = configService.getDataServiceURL();
        }

        function getResource(baseResource) {
            var resource = vm.dataServiceURL + baseResource;

            return $http.get(resource).then(function (response) {
                if (typeof response.data == 'object') {
                    // Got valid response
                    return $q.resolve(response.data);
                }
                else {
                    // Invalid response
                    return $q.reject(response.data);
                }
            }, function (response) {
                // Something went wrong
                return $q.reject(response.data);
            });
        }
        init();

        return factory;
    };

    factory.$inject = injectParams;

    angular.module('mock.dataService', []).factory('mockDataService', factory);
}());

Now for the Jasmine-Karma Unit test.

describe("HTTP Backend Mock testing", function () {

    beforeEach(angular.mock.module("app"));
    beforeEach(angular.mock.module("mock.FakeBackendService"));
    beforeEach(angular.mock.module("mock.configService"));
    beforeEach(angular.mock.module("mock.dataService"));

    it("Get the workflow task", angular.mock.inject(function (mockDataService) {
        var valid = "";

        var promise = mockDataService.getWorkflowTask('http://localhost/foo');

        promise.then(function (response) {
            valid = "Success";
        }, function (response) {
            valid = "Failure";
        });

        expect(valid).toBe("Success");
    }));
});

Now to the question. So, I'll start by saying I'm new to the AngularJS world and even more so to Jasmine. Anyways, when I debug the unit test I find that the promise's status is still 0 and I always get expected '' to be 'Success' telling my I never resolve (hopefully I'm using the right lingo) the promise from the $http service in mockDataService. I've tried playing around with it some and tried to see if anyone has done this kind of a thing before. I found plenty of examples where the $httpBackend is mocked in the test but none like what I'm attempting. Any ideas or suggestions would be great. Thanks.

EDIT got a slightly working solution

So I decided that I'd by pass the run() service and just do the same response in the expectGET().respond().

describe("HTTP Backend Mock testing", function () {
    beforeEach(angular.mock.module("app"));
    beforeEach(angular.mock.module("mock.FakeBackendService"));
    beforeEach(angular.mock.module("mock.configService"));
    beforeEach(angular.mock.module("mock.dataService"));

    it("Get the workflow task", angular.mock.inject(function (mockDataService, $httpBackend, FakeBackendService) {
        var valid = "";
        $httpBackend.expectGET('http://server:80/api/foo').respond(200, FakeBackendService.getWorkflowTasks());
        var promise = mockDataService.getWorkflowTask('foo');


        promise.then(function (response) {
            valid = "Success";
        }, function (response) {
            valid = "Failure";
        });
        $httpBackend.flush();

        expect(valid).toBe("Success");
    }));
});

This sort of solves my testing problem with the run() as the goal was to verify 1) That the regex matching call the correct FakeBackendService and 2) That FakeBackendService returns correct file and actually loads it. I think I can do that by mimicking the same regex in the expectGET. However, I'll leave open for a bit to see if anyone knows how to get the run() to work.

2 Answers 2

1

The promise is not going to resolve unless you force it to do so before the test ends. Here is one such way to do it:

    $httpBackend.expectGET(......).respond(200, 'abc');

    var promise = mockDataService.getWorkflowTask('http://localhost/foo');

    promise.then(function (response) {
        valid = "Success";
    }, function (response) {
        valid = "Failure";
    });

    //new code here
    $httpBackend.flush();

    expect(valid).toBe("Success");

This will force the promise to resolve and your test should pass. You'll also need to inject the $httpBackend service into the test.

Sign up to request clarification or add additional context in comments.

6 Comments

I tried and I get Error: Unexpected request: GET blah blah blah No more request expected
progress... now before you call mockDataService.getWorkflowTask(.....) put an $httpBackend.expectGET(blah blah blah);
I appreciate it Austin, I tried and I'm getting Error: No response defined !
$httpBackend.expectGET(blah blah blah).respond(200, {response: 'object'});
then remove the $httpbackend stuff from the test
|
0

angular.module('mock.dataService', [])
  .service('mockDataService', function($http) {
    this.getWorkflowTask = function(url) {
      return $http.get(url)
    }
  })

describe('HTTP Backend Mock testing', function() {
  var $httpBackend
  beforeEach(angular.mock.module("mock.dataService"));
  beforeEach(inject(function(_$httpBackend_) {
    $httpBackend = _$httpBackend_
  }))

  it("Get the workflow task", angular.mock.inject(function(mockDataService) {
    $httpBackend.expectGET('http://localhost/foo').respond(200);

    var promise = mockDataService.getWorkflowTask('http://localhost/foo');

    promise.then(function(response) {
      valid = "Success";
    }, function(response) {
      valid = "Failure";
    });

    $httpBackend.flush();

    expect(valid).toBe("Success");
  }));
})
<link href="//safjanowski.github.io/jasmine-jsfiddle-pack/pack/jasmine.css" rel="stylesheet" />
<script src="//safjanowski.github.io/jasmine-jsfiddle-pack/pack/jasmine-2.0.3-concated.js"></script>
<script src="//ajax.googleapis.com/ajax/libs/angularjs/1.2.23/angular.min.js"></script>
<script src="//ajax.googleapis.com/ajax/libs/angularjs/1.2.23/angular-mocks.js"></script>

Comments

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.