Reputation: 3520
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:
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:
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!
Upvotes: 4
Views: 3095
Reputation: 812
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:
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 {}; }
};
},
};
});
Upvotes: 2