May 9, 2018
  • All

Ionic Native Mocks

Mike Hartington

Director of Developer Relation...

This is a guest post from Chris Griffith. Chris is UI/UX engineer, an educator in the Ionic community and author of the Ionic Native Mocks library.

When you create an Ionic application, often you have need to include a Cordova plugin or two. In fact, several Cordova plugins are included when you generate an Ionic application using any of the Ionic starter templates. These plugins give your Ionic application access to features on our mobile devices that are traditionally unavailable to developers not using native programming languages.

Currently, there are over 3,100 plugins registered for Cordova. Of these, the Ionic team has selected around 160 for which to create TypeScript interfaces, Ionic Native, to ease development. Here is the definition:

Ionic Native is a curated set of wrappers for Apache Cordova plugins that make adding any native functionality you need to your Ionic mobile application easier.
Ionic Native provides an interface for all of these plugins that match the Cordova plugin, wrapping plugin callbacks in a Promise or Observable to make it easy to use plugins with Angular change detection.

From One Platform to Another

Once you’re using Ionic Native, your workflow must now shift from using your computer’s browser to either running in a device simulator or on an actual device. While the Ionic CLI makes some of that process easier, it can still become time consuming.

Here is a real-world example of this issue: I was prototyping a new app where one of the first actions the user would do was scan in a barcode. Adding this functionality is not difficult. I was able to install the plugin and the Ionic Native wrapper, update the app.module.ts file and was good to go. But from that point further I was required to run my application on the device. Since there were several more screens to create, I needed to find a way to bypass this ‘requirement’.

One option would have been to simply comment out that code block and continue developing, but that would require a lot of maintenance; comment the code to develop, remember to uncomment the code before testing or deploying to a device, uncommenting the code again to do more development. Instead of going through that several times a day, I decided to create a mock provider for the barcode scanner plugin that would serve as in interface to the methods and properties of the actual Ionic Native wrapper’s calls to the Cordova plugin. With this mock injected into my application, I can simulate interaction with the barcode scan plugin and continue my local development unimpeded..

Adding mocks to an App

From this initial effort, I decided to generate mocks for the entire Ionic Native Library in a collection of mocks called Ionic Native Mocks. To use an Ionic Native Mock run the following command in your terminal to install the appropriate mock for your project:

npm install @ionic-native-mocks/<plug-in> --save

For instance, to install the camera mock:

npm install @ionic-native-mocks/camera --save

You also need to install the Ionic Native package for each plugin you want to add. Please see the Ionic Native documentation for complete instructions on how to add and use the plugins.

To use a plugin, import and add the plugin provider to your @NgModule, and then inject it where you wish to use it.

// app.module.ts
import { Camera } from '@ionic-native/camera';
import { CameraMock } from '@ionic-native-mocks/camera';
...

@NgModule({
  ...

  providers: [
    ...
    { provide: Camera, useClass: CameraMock }
  ]
  ...
})
export class AppModule { }

// elsewhere in your app
import { Platform } from 'ionic-angular';
import { Camera, CameraOptions } from '@ionic-native/camera';


@Component({ ... })
export class MyComponent {

  constructor(private camera: Camera, private platform: Platform) {

    platform.ready().then(() => {

    const options: CameraOptions = {
        quality: 100,
        destinationType: this.camera.DestinationType.DATA_URL,
        encodingType: this.camera.EncodingType.JPEG,
        mediaType: this.camera.MediaType.PICTURE
    }

      this.camera.getPicture(options).then((imageData) => {
        // imageData is either a base64 encoded string or a file URI
        // If it's base64:
        console.log(imageData);
        let base64Image = 'data:image/jpeg;base64,' + imageData;
    }, (err) => {
        // Handle error
    });
    });
  }
}

By design, the Ionic Native Mocks I wrote are very generic. They return the bare minimum amount of data for them to function. For them to be more useful in your project, you may want to customize them. For example, for the bar code scanner, I wanted the mock to always return a valid product code.

To begin modifying an Ionic Native Mock file, you will first need to get the code directly from GitHub and the source Typescript code and add it to your project manually. While each plugin is available via npm, those files are installed in your project’s “node_modules” folder and can easily get overwritten or deleted.

In your project, create a new directory named mocks, and create another directory named barcodescanner. Within that directory, download the index.ts file from Github into this directory.

Now let’s adjust out app.module.ts file. Like all Ionic Native modules, we need to import it.

import { BarcodeScanner } from '@ionic-native/barcode-scanner';

Also import our plugin mock as well.

import { BarcodeScannerMock } from '../mocks/barcodescanner';

Instead of including the Ionic Native plugin directly into the providers array, we instead tell Angular to provide a mapping to our mock. This allows us to keep the rest of application referencing the real Ionic Native module, yet use the code from the mock instead.

{ provide: BarcodeScanner, useClass: BarcodeScannerMock }

At this point we could build our app, making calls to the barcode scanner plugin without needing to test on an actual device. But, out of the box, the barcode scanner mock is going to return an empty string.

But, let’s have it return something that we might want our user to scan. For my original app, it was a QR code on our packaging. Open the index.ts file within our project and change the scan function to

scan(options?: BarcodeScannerOptions): Promise {
  let code='NCC-1701';
  let theResult:BarcodeScanResult= {format:'QR_CODE', cancelled:false, text:code };

  return new Promise((resolve, reject) => {
    resolve(theResult);
  });
}

Save the file, and run the application. Now when you call the barcode scanner, it will return your custom data. When you are ready to use the real plugin, change the provider and remove the import of the mock.

Parting words

Hopefully, this post has shown you how to leverage Ionic Native Mocks into your Ionic application, and how it can reduce the friction during the build & test cycle of development. The mocks are are open source, and if you find an issue, please let me know. Happy Coding!


Mike Hartington

Director of Developer Relation...