0

According to the Angular docs for ngMock the $httpBackend when method should intercept $http service requests and serve the specified response. I assume that the mock $httpBackend methods would be synchronous to make testing easier. But result.test ends up being undefined.

describe('Http requests', function () {
    var scope, $httpBackend, constituents;

    beforeEach(module('main'));

    beforeEach(inject(function (_$httpBackend_, _constituents_) {
        constituents = _constituents_;
        $httpBackend = _$httpBackend_;
        $httpBackend.expectGET("App/Main/login.html").respond(200);
    }));

    it('Should get the constituents', function () {
        $httpBackend.whenGET(webServicesPath + "api/constituents/all-constituents").respond(200, { "test": true });
        var result = constituents.getAllConstituents();
        $httpBackend.flush();
        expect(result.$$state.value.test).toEqual(true);
    });

});

I tried using $httpBackend.flush() but that has unintended consequences leading to this...

Error: Unexpected request: GET App/Main/login.html

which means the ui.routing service got invoked somehow. so I handled that by adding a $httpBackend.expectGET... in the beforeEach.

Why do I have to even use the flush method? Seems overly complicated. Why does it trigger ui.routing when that has nothing to do with my unit test?

For reference, this is the factory used

app.factory('constituents', ['$http', '$log', function ($http, $log) {
    function getAllConstituents() {
        return $http.get(webServicesPath + "api/constituents/all-constituents").then(
            function (response) {
                return response.data;
            },
            function (response) {
                $log.error("Load Constituents - " + response.status + " " + response.statusText);
                return;
            }
        );
    }
    return {
        getAllConstituents: getAllConstituents
    }
}]);
2
  • Where is this webServicesPath variable coming from? It seems to be undefined in both your spec and your factory. Commented Jul 29, 2015 at 22:11
  • it comes from somewhere else but I have verified that it is defined Commented Jul 29, 2015 at 22:27

1 Answer 1

4

Your results variable is a promise not the result of the $http request. You would have to do something like this to access the result:

it('Should get the constituents', function () {
    var result;

    $httpBackend.whenGET('foo.json').respond(200, { "test": true });

    constituents
      .getAllConstituents()
      .then(function(response){
        result = response;
      });

    $httpBackend.flush();
    expect(result.test).toEqual(true);

});

My question is why do I have to even use the flush method?

Because writing asynchronous unit tests can be hell. So we need to use the flush method to make our asynchronous tests synchronous which makes life much easier as we no longer need to create loads of fake promises and figure out where to put the done() callback that tells our test framework that the test is over etc. etc.

Why does it trigger ui.routing when that has nothing to do with my unit test?

You can read more about that in this SO question

UI-router interfers with $httpbackend unit test, angular js

It can be a pain but I've found that using a helper library like bardJS is probably the quickest way to make ui-router problems go away and it also removes a lot of the boilerplate that you need to write for Angular unit tests.

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

1 Comment

Huge thanks for this. I am wondering how much bard.asyncModule would reduce/change the current answer you produced?

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.