What's wrong with Angular service test?

626 views
Skip to first unread message

ksenia.m...@gmail.com

unread,
Nov 27, 2012, 4:52:15 AM11/27/12
to ang...@googlegroups.com
Hi everyone,

I've got a service calling external web service:
angular.module('myApp.services', [])
.service('autoCmpltDataSvc', function ($http) {
    var innerMatch = function (data) {
        return $.map(data, function (item) {
            return {
                fullName: item.name + (item.adminName1 ? ", " + item.adminName1 : "") + ", " + item.countryName,
                shortName: item.name,
                itemId: item.geonameId
            };
        });
    };

    this.fetchFromGeonamesDb = function (request, response, matcher) {
        $http({
            method: 'jsonp',
            params: {
                featureClass: "P",
                style: "full",
                maxRows: 12,
                name_startsWith: request.destName
            }
        }).success(function (data, status) {
            console.log(data);
            response($.map(innerMatch(data.geonames), matcher));
        });
    };
});

I'm trying to test that it correctly forms the output, so I mock the call to real web service. 
Here is my unit-test.
describe('Services', function () {

    beforeEach(module('myApp.services'));

    describe('autoCompleteService', function () {
        var $httpBackend, svc;
        var results = [];
        var matcher = function (item) {
            return item;
        };
        var response = function (arr) {
            results = arr;
        };

        beforeEach(inject(function ($injector, autoCmpltDataSvc) {
            svc = autoCmpltDataSvc;
            $httpBackend = $injector.get('$httpBackend');
            $httpBackend.whenJSONP(/searchJSON/).
              respond([
              { name: 'City1', adminName1: 'Region1', countryName: 'Country1', geonameId: 1 },
              { name: 'City2', countryName: 'Country2', geonameId: 2}]);
        }));

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

        it('should return values', function () {
            $httpBackend.expectJSONP(/searchJSON/);
            svc.fetchFromGeonamesDb({ 'destName': 'fra' }, response, matcher);
            $httpBackend.flush();
            expect(results.length).toBe(2);
        });
    });
});
But the test produces an error.
        TypeError: Cannot read property 'length' of undefined
            at Function.v.extend.map (/app/lib/jquery-1.8.3.min.js:2:15334)

I suppose it's something wrong with mock respond, as it seems it doesn't return the array.
But I can't understand why it doesn't return the array.

Thanks in advance!

Peter Bacon Darwin

unread,
Nov 27, 2012, 5:05:35 AM11/27/12
to ang...@googlegroups.com
Don't run unit tests with minified code.  Try removing jQuery altogether and see what you get.  Also try hitting the JSONP url directly in your unit test to see what the mock actually returns.
Pete


--
You received this message because you are subscribed to the Google Groups "AngularJS" group.
To post to this group, send email to ang...@googlegroups.com.
To unsubscribe from this group, send email to angular+u...@googlegroups.com.
Visit this group at http://groups.google.com/group/angular?hl=en-US.
 
 

ksenia.m...@gmail.com

unread,
Nov 27, 2012, 5:33:35 AM11/27/12
to ang...@googlegroups.com
Oh yeah, of course :)

angular.module('myApp.services', [])
.service('autoCmpltDataSvc', function ($http) {
    var innerMatch = function (data) {
        return $.map(data, function (item) {  // line 8
            return {
                fullName: item.name + (item.adminName1 ? ", " + item.adminName1 : "") + ", " + item.countryName,
                shortName: item.name,
                itemId: item.geonameId
            };
        });
    };

    this.fetchFromGeonamesDb = function (request, response, matcher) {
        $http({
            method: 'jsonp',
            params: {
                featureClass: "P",
                style: "full",
                maxRows: 12,
                name_startsWith: request.destName
            }
        }).success(function (data, status) {
            console.log(data);
            response($.map(innerMatch(data.geonames), matcher));
        });
    };
});

Peter Bacon Darwin

unread,
Nov 27, 2012, 5:44:22 AM11/27/12
to ang...@googlegroups.com
OK so your success function is expecting your http call to return an object with a field called geonames, currently your mock is returning a straight array.

Perhaps you should change your mock to this?

$httpBackend.whenJSONP(/searchJSON/).
              respond( { geonames: [
                { name: 'City1', adminName1: 'Region1', countryName: 'Country1', geonameId: 1 },
                { name: 'City2', countryName: 'Country2', geonameId: 2}]
              });

ksenia.m...@gmail.com

unread,
Nov 27, 2012, 5:51:51 AM11/27/12
to ang...@googlegroups.com
Thank you, this did help!
I really didn't notice that the real server returned object with geonames field rather than array.

BTW, is there any way to debug the unit tests?

Pawel Kozlowski

unread,
Nov 27, 2012, 6:00:52 AM11/27/12
to ang...@googlegroups.com
Hi!

On Tue, Nov 27, 2012 at 11:51 AM, <ksenia.m...@gmail.com> wrote:

> BTW, is there any way to debug the unit tests?

If you are running those tests in a real browser (you are using
Testacular, right?) just add the 'debugger' statement:

debugger;

t so the execution is suspended, browser is paused so you can debug
(make sure that the developer tools / firebug or whatever else you are
using is open, otherwise execution won't stop).

Vojta also had a video showing how to debug from an IDE:
https://github.com/vojtajina/testacular

but I didn't set it up myself, always debugging in a browser.

Cheers,
Pawel

ksenia.m...@gmail.com

unread,
Nov 27, 2012, 6:32:27 AM11/27/12
to ang...@googlegroups.com
Cool, thanks! It worked
Reply all
Reply to author
Forward
0 new messages