Vincent
Vincent

Reputation: 6188

Testing asynchrone function gives Unexpected request

The unittest:

"use strict";

var usersJSON = {};

describe("mainT", function () {


 var ctrl, scope, httpBackend, locationMock, 

    beforeEach(module("testK"));
    beforeEach(inject(function ($controller, $rootScope, $httpBackend, $location, $injector) {
        scope = $rootScope.$new();
        httpBackend = $httpBackend;
        locationMock = $location;

        var lUrl = "../solr/users/select?indent=true&wt=json",
        lRequestHandler = httpBackend.expect("GET", lUrl);          
        lRequestHandler.respond(200, usersJSON);     

        ctrl = $controller("mainT.controller.users", { $scope: scope, $location: locationMock});
        httpBackend.flush();
        expect(scope.users).toBeDefined();

    }));

    afterEach(function () {
        httpBackend.verifyNoOutstandingRequest();
        httpBackend.verifyNoOutstandingExpectation();
    });




        describe("method test", function () {
        it('should test', function () {
            expect(true).toBeFalsy();
        });
    });
});

controller I'm testing (working): Asynchrone function in init who's giving me trouble (uses ../solr/users/select?indent=true&wt=json):

 $scope.search = function () {
                    var lStart = 0,
                        lLimit = privates.page * privates.limit;


                    Search.get({
                        collection: "users",
                        start: lStart,
                        rows: lLimit)
                    }, function(records){
                        $scope.users= records.response.docs;
                    });
                };

What I think happens:
1. inform backend what request he will receive
2. inform backend to response on that request with empty JSON
3. create a controller (Search.get get's executed)
4. inform backend to receive all requests and answer them (flush)

Yet I always get the following error:

Error: Unexpected request: GET : ../solr/users/select?indent=true&wt=json

Am I not handling the asynchrone search function well? how should this be done?

Upvotes: 4

Views: 1641

Answers (3)

Ben Lesh
Ben Lesh

Reputation: 108501

That's not really a "unit" test, it's more of a behavioral test.

This should really be a few tests:

  1. Test your service Search.get to make sure it's calling the proper URL and returning the result.
  2. Test your controller method to make sure it's calling Search.get
  3. Test your controller method to make sure it's putting the result in the proper spot.

The code you've posted is a little incomplete, but here are two unit tests that should cover you:

This is something I've blogged about extensively, and the entries go into more detail:

Here's an example of what I'm talking about:

describe('Search', function () {
    var Search,
        $httpBackend;

    beforeEach(function () {
        module('myModule');

        inject(function (_Search_, _$httpBackend_) {
            Search = _Search_;
            $httpBackend = _$httpBackend_;
        });
    });

    describe('get()', function () {
        var mockResult;

        it('should call the proper url and return a promise with the data.', function () {
            mockResult = { foo: 'bar' };
            $httpBackend.expectGET('http://sample.com/url/here').respond(mockResult);

            var resultOut,
                handler = jasmine.createSpy('result handler');
            Search.get({ arg1: 'wee' }).then(handler);

            $httpBackend.flush();

            expect(handler).toHaveBeenCalledWith(mockResult);

            $httpBackend.verifyNoOutstandingRequest();
            $httpBackend.verifyNoOutstandingExpectation();


        });
    });

});

describe('myCtrl', function () {
    var myCtrl,
        $scope,
        Search;

    beforeEach(function () {
        module('myModule');

        inject(function ($rootScope, $controller, _Search_) {
            $scope = $rootScope.$new();
            Search = _Search;
            myCtrl = $controller('MyCtrl', {
                $scope: scope
            });
        });
    });

    describe('$scope.foo()', function () {
        var mockResult = { foo: 'bar' };

        beforeEach(function () {
            //set up a spy.
            spyOn(Search, 'get').andReturn({
                then: function (fn) {
                    // this is going to execute your handler and do whatever
                    // you've programmed it to do.. like $scope.results = data; or
                    // something.
                    fn(mockResult);
                }
            });

            $scope.foo();
        });

        it('should call Search.get().', function () {
            expect(Search.get).toHaveBeenCalled();
        });

        it('should set $scope.results with the results returned from Search.get', function () {
            expect(Search.results).toBe(mockResult);
        });
    });

});

Upvotes: 3

MBielski
MBielski

Reputation: 6620

In a BeforeEach you should use httpBackend.when instead of httpBackend.expect. I don't think you should have an assertion (expect) in your BeforeEach, so that should be moved to a separate it() block. I also don't see where lRequestHandler is defined. The 200 status is sent by default so that is not needed. Your httpBackend line should look like this:

httpBackend.when("GET", "/solr/users/select?indent=true&wt=json").respond({});

Your test should then be:

    describe("method test", function () {
        it('scope.user should be defined: ', function () {
            expect(scope.user).toEqual({});
        });
    });

Upvotes: 2

Miichi
Miichi

Reputation: 1799

Your lUrl in the unit test, shouldn't be a relative path, i.e., instead of "../solr/users/select?indent=true&wt=json" it should be an absolute "/solr/users/select?indent=true&wt=json". So if your application is running at "http://localhost/a/b/index.html", lUrl should be "/a/solr/...".

Note that you can also use regular expressions in $httpBackend.expectGET(), that could be helpful here in case you are not entirely sure how the absolute path will look like later on.

Upvotes: 2

Related Questions