Testing Geolocation Apps Without Moving in Ionic

Ionic Native Geolocation is great, however, when we need to test different use cases on a real device it becomes a bit complicated.

In this tutorial we will create a special mock that will allow us to stay at home while easily spoofing to other places.

Mock Selection

Let's get the party started:

ionic start ionic-mock-geolocation blank

The classic Ionic & Cordova plugins:

ionic cordova plugin add cordova-plugin-geolocation npm install --save @ionic-native/geolocation

As you might know, everything is bootstrapped in the app.module.ts file, that's where the magic will start:

import { Geolocation } from '@ionic-native/geolocation' ; import { GeolocationMock } from './geolocation/geolocation.mock' ; @ NgModule ( { . . . providers : [ . . . { provide : Geolocation , useClass : GeolocationMock } ] } ) export class AppModule { }

We start by importing the Geolocation Class and the GeolocationMock Class (no worry for this one, we will create it later).

When bootstrapping the application, the providers property will stock a list of the available Services.

We can use one trick to redirect every call from one Service to another and that's what is done here. Instead of using the Geolocation Class, the application will use the GeolocationMock Class.

Mock Creation

We can now create the geolocation.mock.ts file in a new geolocation folder:

import { Geolocation } from "@ionic-native/geolocation" ; export class GeolocationMock extends Geolocation { latitude = 1 ; longitude = 1 ; getCurrentPosition ( ) { return new Promise ( ( resolve , reject ) = > { resolve ( { coords : { latitude : this . latitude , longitude : this . longitude } } ) ; } ) ; } setLatitude ( latitude ) { this . latitude = latitude ; console . log ( "Latitude changed to: " , latitude ) ; } setLongitude ( longitude ) { this . longitude = longitude ; console . log ( "Longitude changed to: " , longitude ) ; } }

We start by extending the Geolocation Class to keep everything consistent.

This class has two property that will allow us to move: latitude and longitude.

Those property will be modified using the setters: setLatitude and setLongitude.

Finally the application will need the getCurrentPosition method so we implement our own.

We need to respect the behavior of the Geolocation, when having a look at the getCurrentPosition method in VisualStudio:

A Promise is supposed to be returned, an ES6 Promise will do the job. The data that is resolved also matches the Geoposition structure (latitude and longitude inside of a coords object). The latitude and longitude will depend on the latitude and longitude properties.

And ... that's it, the mock is ready and loaded.

Mock Usage

We now create our navigating interface in the home.html file:

< ion-content padding > < ion-item > < ion-label > Latitude: </ ion-label > < ion-input [(ngModel)] = " latitude " > </ ion-input > </ ion-item > < ion-item > < ion-label > Longitude: </ ion-label > < ion-input [(ngModel)] = " longitude " > </ ion-input > </ ion-item > . . . </ ion-content >

Starting with two inputs that will change the longitude and latitude.

Followed by displaying the coords property where the position data will be received:

< div text-center > Your position: {{coords.latitude}} , {{coords.longitude}} </ div >

And three buttons:

< div text-center > < button ion-button (click) = " setLatitude(latitude) " > Set latitude </ button > < button ion-button (click) = " setLongitude(longitude) " > Set longitude </ button > < button ion-button (click) = " getCurrentPosition() " > Get current position </ button > </ div >

Allowing us to set a new latitude, longitude and updating the coords property.

Great!

The missing piece here is the home.ts file which links the template to the Geolocation mock:

import { Geolocation } from '@ionic-native/geolocation' ; export class HomePage { coords ; constructor ( public navCtrl : NavController , public geolocation : Geolocation ) { this . getCurrentPosition ( ) ; } . . . }

We start by importing and injecting the Geolocation Service. Remember when we say:

Hey give me the Geolocation Service.

We get the Geolocation Mock class.

Note that the coords property is also declared.

The getCurrentPosition method is used in the constructor, which is this one:

getCurrentPosition ( ) { this . geolocation . getCurrentPosition ( ) . then ( ( resp ) = > { this . coords = resp . coords ; } ) . catch ( ( error ) = > { console . log ( 'Error getting location' , error ) ; } ) ; }

Using the geolocation property, the coords property is set once the Promise is resolved.

By default, the latitude and longitude values are set to 1.

To modify those values, we can now add the following methods:

setLongitude ( longitude ) { ( < any > this . geolocation ) . setLongitude ( longitude ) ; } setLatitude ( latitude ) { ( < any > this . geolocation ) . setLatitude ( latitude ) ; }

Receiving the values from the home.html file, the geolocation service will update its latitude and longitude values. The (true) Geolocation Service doesn't have the setLatitude and setLongitude methods so TypeScript will shout if we do nothing.

The type of the geolocation object is quickly changed to any to prevent this.

And voila!

We can modify the Geolocation and fly all around the world!

Conclusion

Mocking in Ionic is very easy.

It all starts in the app.module.ts file notifying the app to replace the real class by our mock.

Once this is done, importing the real class will result in acquiring the mock. I chose the Geolocation plugin for example because the next tutorial will need this feature, but any Service can be mocked in Ionic especially for Unit Testing so keep this solution in the back of your mind, it's highly likely you will need it.