Unit Testing in Ionic

How to Unit Test an Ionic 2 Application


Everybody (hopefully) tests their applications in some manner before submitting them to the app stores, and for a lot of people, this will include playing around with the app and trying it on different devices. That alone is a poor and inefficient testing process, however.

Unit testing, on the other hand, is a very efficient testing process. Unit testing is where you take small testable chunks (units) of an application and test that they work properly. Essentially, you write code to automatically test your code for you.

Angular 2 and Ionic 2 are very modular by nature (i.e. most features in an Ionic 2 app are their own little independent components that work together with other components) so unit testing is quite easy to set up.

This obviously requires more development effort, as you need to write tests as well as the code itself. So why might we want to invest in doing that? The main benefits of adding unit tests to your application are:

  • Documentation – unit tests are set out in such a way that they accurately describe the intended functionality of the application
  • Testing – of course, this is a great way to test that your application is working as intended
  • Regression Testing – when you make changes to your application you can be more confident that you haven’t broken anything else, and if you have you’ll likely know about it
  • Sleep – you’ll be less of a nervous wreck when deploying your application to the app store

Arguably, someone like myself who is a freelancer working primarily on small mobile applications won’t see as much benefit from setting up automated tests as a large team developing the next Dropbox. But even as a freelancer I’ve taken on some projects that started out small and well defined enough but grew into monsters that I spent hours manually testing and fixing for every update. If I had’ve taken the time to set up unit testing my life could have been a lot easier.

If you’re looking for a little more background on unit testing in general, take a look at: An Introduction to Unit Testing in AngularJS Applications.

In this tutorial I am going to show you how you can set up simple unit testing with Jasmine and Karma in your Ionic 2 applications. Here’s what we’ll be building:

Ionic 2 Magic Ball

We’re going to start off by setting up a really simple test for one service in the application, but we will likely expand on this in future tutorials.

UPDATE: If you would like a more advanced introduction to testing Ionic 2 application, I would recommend reading my Test Driven Development in Ionic 2 series.

Before we Get Started

Before you go through this tutorial, you should have at least a basic understanding of Ionic 2 concepts. You must also already have Ionic 2 set up on your machine.

If you’re not familiar with Ionic 2 already, I’d recommend reading my Ionic 2 Beginners Guide first to get up and running and understand the basic concepts. If you want a much more detailed guide for learning Ionic 2, then take a look at Building Mobile Apps with Ionic 2.

1. Generate a New Ionic 2 Application

The application we are going to build will simulate a “Magic 8 Ball”. Basically, the user will be able to write a question, hit a button, and an answer to their question will be “magically” calculated (i.e. chosen at random).

Let’s start off by generating a new application with the following command:

ionic start ionic2-magic-ball blank --v2

Once that has finished generating make it your current directory:

cd ionic2-magic-ball

and then generate the “MagicBall” provider with the following command:

ionic g provider MagicBall

So that we can use this provider throughout the application, we will need to add it to the app.module.ts file.

Modify src/app/app.module.ts to reflect the following:

import { NgModule, ErrorHandler } from '@angular/core';
import { IonicApp, IonicModule, IonicErrorHandler } from 'ionic-angular';
import { MyApp } from './app.component';
import { HomePage } from '../pages/home/home';
import { MagicBall } from '../providers/magic-ball';

  declarations: [
  imports: [
  bootstrap: [IonicApp],
  entryComponents: [
  providers: [{provide: ErrorHandler, useClass: IonicErrorHandler}, MagicBall]
export class AppModule {}

2. An Introduction to Jasmine

As I mentioned before, we will be using Jasmine and Karma to unit test our application. Jasmine is what we use to create the unit tests, and Karma is what runs them. We will need to set both of these up in our application before we can use them, but first, let’s talk a little bit about how Jasmine works.

Jasmine is a framework for writing code that tests your code. It does that primarily through the following three functions: describe, it, and expect:

  • describe() defines a suite of tests (or “specs”)

  • it() defines a test or “spec”, and it lives inside of a suite (describe()). This is what defines the expected behaviour of the code you are testing, i.e. “it should do this”, “it should do that”

  • expect() defines the expected result of a test and lives inside of it().

So a skeleton for a test might look something like this:

describe('My Service', () => {

    it('should correctly add numbers', () => {

        expect(1 + 1).toBe(2);



In this example we have a test suite called “My Service” that contains a test for correctly adding numbers. We use expect to check that the result of 1 + 1 is 2. To do this we use the toBe() matcher function which is provided by Jasmine. There’s a whole range of these methods available for testing different scenarios, for example:

  • expect(fn).toThrow(e);
  • expect(instance).toBe(instance);
  • expect(mixed).toBeDefined();
  • expect(mixed).toBeFalsy();
  • expect(number).toBeGreaterThan(number);
  • expect(number).toBeLessThan(number);
  • expect(mixed).toBeNull();
  • expect(mixed).toBeTruthy();
  • expect(mixed).toBeUndefined();
  • expect(array).toContain(member);
  • expect(string).toContain(substring);
  • expect(mixed).toEqual(mixed);
  • expect(mixed).toMatch(pattern);

and if you want to get really advanced you can even define your own custom matchers. The example we have used is just for demonstration and is a bit silly, because we have manually supplied values which will of course pass the test. When we create tests for a real world scenario shortly it should help clarify how you might actually create a unit test for your application.

3. Setting up Jasmine and Karma

Please follow this guide for setting up testing in your Ionic 2 project. You do not have to read or complete the entire tutorial, you will just need to follow the ‘2. Set up Testing’ section.

4. Create and Run a Unit Test

It’s finally time to create our first test. You may have heard of the term “Test Driven Development”. Basically it’s a development process where the tests are written first, and then the actual code is written afterwards. This helps define requirements and ensures that tests are always created. So to stick with the theme, we’re going to have a go at that ourselves. The process goes like this:

  1. Write a test
  2. Run the test (it will fail)
  3. Write your code
  4. Run the test (it will pass, hopefully)

Create a new file at providers/magic-ball.spec.ts and add the following:

import { MagicBall } from './magic-ball';

describe('Magic 8 Ball Service', () => {

    it('should do nothing', () => {




We’ve set up a really basic test here. We import our MagicBall service and then we have created a test that will always pass. If we were to run npm test now we would see something like this:

Unit Test 1

Modify magic-ball.spec.ts to reflect the following:

import { MagicBall } from './magic-ball';

describe('Magic 8 Ball Service', () => {

    it('should do nothing', () => {

        expect(true).not.toBeTruthy(); //can also use .toBeFalsy();



Now we have negated the test using .not (alternatively, we could have used toBeFalsy), so that it will always fail. Now if we ran npm test we would see something like this:

Unit Test 2

Modify magic-ball.spec.ts to reflect the following:

import { MagicBall } from './magic-ball';

describe('Magic 8 Ball Service', () => {

    it('should do nothing', () => {

        expect(1 + 1).toBe(2);
        expect(2 + 2).toBe(5); //this will fail



One last example before we get into the real test. You can add multiple conditions to each test and if any of them fail the test will fail. In this case we have two that will pass, but one that will fail. If we were to run npm test with this we would see the following:

Unit Test 3

Notice that the error messages says “Expected 4 to be 5”, which was our failing test. Now let’s define our real tests.

Modify magic-ball.spec.ts to reflect the following:

import { MagicBall } from './magic-ball';

let magicBall = null;

describe('Magic 8 Ball Service', () => {

    beforeEach(() => {
      magicBall = new MagicBall();

    it('should return a non empty array', () => {

            let result = magicBall.getAnswers();


    it('should return one random answer as a string', () => {
            expect(typeof magicBall.getRandomAnswer()).toBe('string');

    it('should have both yes and no available in result set', () => {

            let result = magicBall.getAnswers();




We have defined three tests here, which:

  • Test that the getAnswers method returns a non-empty array
  • Test that getRandomAnswer returns a string
  • Test that both ‘Yes’ and ‘No’ are in the result set

Also, notice that the tests are a bit more complicated now. Since we are using the MagicBall service it needs to be injected into our tests. We do this by using beforeEach (which runs before each of the tests) to create a fresh instance of our magic ball service for each of the tests. We’re making use of various matchers here, including toContain and toBeGreaterThan.

If you were to run the tests now using npm test you will just get a whole bunch of errors because we haven’t even defined the methods for MagicBall yet. This is what we want, though. We have some tests that don’t pass, now we just need to make them pass.

4. Build the App

Now we’re going to build out the rest of the app, which will involve implementing the MagicBall service, and also creating a simple layout to display the answer.

Modify magic-ball.ts to reflect the following:

import { Injectable } from '@angular/core';

export class MagicBall {

  answers: any;


    this.answers = [
      'All signs point to yes',
      'Try again later',
      'Without a doubt',
      'Don\'t count on it',
      'Most likely',
      'Absolutely not'


    return this.answers;

    return this.answers[this.getRandomInt(0, this.answers.length-1)];

  getRandomInt(min, max){
    return Math.floor(Math.random() * (max - min + 1)) + min;


Pretty simple stuff here, we’ve just manually defined an array with some magic answers, and created a couple of methods to access those.

Modify home.html to reflect the following:

<ion-content padding>


      <ion-input type="text" [(ngModel)]="question" placeholder="Ask the mystic 8 ball anything..."></ion-input>

    <button ion-button full color="dark" (click)="showAnswer()">Click to Decide Fate</button>




This sets up an input field, a button to trigger fetching an answer, and a spot to display the answer. We will also need to set up our class definition to handle fetching and displaying the answers.

Modify home.ts to reflect the following:

import { Component } from '@angular/core';
import { MagicBall } from '../../providers/magic-ball';

  selector: 'page-home',
  templateUrl: 'home.html'
export class HomePage {

  answer: any = "..."

  constructor(public magicBall: MagicBall) {


    this.answer = this.magicBall.getRandomAnswer();


and let’s also pretty things up a bit:

Modify home.scss to reflect the following

page-home {

    .scroll-content {
        background-color: #8e44ad;

    h2 {
        margin-top: 50%;
        text-align: center;
        color: #fff;


5. Run the Tests Again

We’ve finished implementing our MagicBall service now, and if you run it through ionic serve everything seems to be working. However, to make sure we can now run npm test and we should see something like the following:

Unit Test 4

All three tests have passed! If you were to change the answers array so that it didn’t include “Yes” or “No” and re-ran the tests, you would see that one fails.


Unit tests are a great way to test your application, but that isn’t the end of the story. Whilst individual components in an application might work flawlessly, when working together they might fail. This is where Integration Testing comes in, but we will get to that in a future tutorial.

This is also just a very basic example of unit testing, so we will cover some more advanced scenarios in future tutorials as well.

UPDATE: I now have a more advanced series of tutorials on testing available: Test Driven Development in Ionic 2.

What to watch next...