nuander
nuander

Reputation: 1403

AngularJS Unit Test using HttpBackend.whenGet doesn't return expected result

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
    }
}]);

Upvotes: 0

Views: 2805

Answers (1)

Matt Herbstritt
Matt Herbstritt

Reputation: 4862

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.

Upvotes: 4

Related Questions