Question

I've only done a small bit of testing in AngularJS using Jasmine and Karma (test runner) but I am stuck at the moment. I am integrating google maps on a partial view and I am eager to test the functions I wrote for It in the corresponding controller (placing a marker when a click happens, changing the radius of a circle on the map,...)

All the functionality works but testing it seems a lot harder. This is a simple test that I am using to verify that a service is defined:

  it('should contain a locationDbService',
    function () {
        expect(locationDbService).toBeDefined();
    });

I have the following code executed before each test:

var ctrl, scope, locationDbService;
// inject the $controller and $rootScope services
// in the beforeEach block
beforeEach(inject(function ($controller, $rootScope, _LocationDbService_) {
    // Create a new scope that's a child of the $rootScope
    scope = $rootScope.$new();
    // Create the controller
    ctrl = $controller('AddLocationCtrl', {
        $scope: scope,
        LocationDbService: _LocationDbService_
    });
    locationDbService = _LocationDbService_;
}));

The controller header is the following:

.controller('AddLocationCtrl', function ($scope, LocationDbService) {

Initialize function in the controller:

    var map;

    $scope.initialize = function () {
        var mapOptions = {
            zoom: 15,
            center: new google.maps.LatLng(51.142036, 4.440966)
        };

        map = new google.maps.Map(document.getElementById('map-canvas-add-location'), mapOptions);

    };

    google.maps.event.addDomListener(window, "load", $scope.initialize());

View:

            <div class="item item-body">
                <div id="map-canvas-add-location"></div>
            </div>

The problem I encountered at the start was:

Google not defined

I "fixed" it by added the google maps js file to the Karma conf files array, is this a correct solution?

When I add the js file and run the test again I get the following failure:

offsetWidth null

I've been searching the web for a solution for many hours now and can't find any of them to be working. What am I doing wrong?

Thanks in advance!

Was it helpful?

Solution

For your first error screen, you are correct that it is due to the jasmine test runner not containing a reference to the google maps js file.

For the second error you do not have a div element with map-canvas-add-location. Your view does contain the div element, but it isn't being loaded within your jasmine test suite. This is correct behavior, unit tests should not depend on a specific dom structure as it makes them far more brittle.

I know of 2 ways to handle the second error:

  1. Utilizing services to wrap specific calls to google map dependencies. This is how my current angular maps project works.
  2. Converting all map level tests to E2E tests instead of unit tests.

I recommend number 1 as E2E testing only is not a step to be taken lightly. E2E tests are generally more brittle and harder to maintain.

Here is a sample of how a wrapper service might look:

.factory('GoogleMap', function(){
    return {
        createMap: function(element, options){
            return new google.maps.Map(element, options);
        },
        mapOptions: {
            animation: google.maps.MapTypeId.DROP,
            maxZoom: 15,
            mapTypeControlOptions: {
                position: google.maps.ControlPosition.LEFT_BOTTOM,
                style: google.maps.MapTypeControlStyle.DROPDOWN_MENU
            }
        },
        markerOptions:{
            animation: google.maps.Animation.DROP,
            clickable: true
        },
        position: function(location){
            return new google.maps.LatLng(location.latitude, location.longitude);
        },
        geocode: function(search, successFun){
            var geocoder = new google.maps.Geocoder();
            geocoder.geocode(search, successFun);
        },
        bounds: function(){
            return new google.maps.LatLngBounds();
        }
    };
});

and used within code:

var mapOptions = angular.extend({
        zoom: 15,
        center: GoogleMap.position({latitude: 51.142036, longitude: 4.440966})
    }, GoogleMap.mapOptions);
map = GoogleMap.createMap(document.getElementById('map-canvas-add-location'), mapOptions);

Then in your tests you'll need to create a mock GoogleMap service and setups spies on that mock to assert your map is being correctly generated.

Here is a sample mock GoogleMap service:

.factory('mockGoogleMap', function(){
    var mapResult = {
        formatted_address: 'mock formatted address',
        geometry: {
            location: {
                lat: function(){
                    return 1;
                },
                lng: function(){
                    return 2;
                }
            }
        }
    };
    return {
        createMap: function(element, options){
            return {
                //add functions called by your controller on the map here
            };
        },
        mapOptions: {},
        markerOptions: {},
        position: function(location){
            return {latitude: location.latitude, longitude: location.longitude};
        },
        geocode: function(params, success){
            success([mapResult], null);
        },
        geocodeResult: mapResult,
        bounds: function(){
            return {
                extend: function(position){},
                getCenter: function(){ return {}; }
            };
        },
    };
});
Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top