Testing in Ionic 2

E2E (End-to-End) Testing in Ionic 2: An Introduction



·

Over the past few weeks, I have been releasing a series of tutorials on creating automated tests for Ionic 2 applications. The tutorials so far have focused on how to apply the Test Driven Development (TDD) methodology to unit test an Ionic 2 application.

If the concept of automated tests is new to you, or if you don’t understand why you would want to create automated tests for an application, I would recommend reading through my Test Driven Development series of tutorials.

In this tutorial, we will be covering another type of automated test called End-to-End Testing or E2E for short, and how to apply that to an Ionic 2 application. We will just be focusing on the bare bones set up, for now, to get a simple E2E test running, I will cover strategies for creating effective E2E tests in future tutorials (like how to incorporate E2E tests into a TDD approach alongside your unit tests).

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 setup on your machine.

You should also have a basic understanding of Jasmine and creating unit tests, as I will not be covering the theory behind creating tests with Jasmine in this tutorial. If these concepts are new to you, you should read this first.

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.

What is an E2E Test?

So far, we have covered unit tests which test isolated units of your code. You might create tests for a single class in isolation to make sure its methods do what they are supposed to do. You don’t worry about any dependencies that class might have or anything it interacts with, all we are interested in is if that one unit is doing what it is supposed to do.

To create unit tests in Ionic 2 we use two tools: Jasmine to create the tests, and Karma to automatically run the tests through a browser.

An E2E test, on the other hand, allows us to test interactions between various components in our application. Specifically, it allows us to test the application in a way that the user would actually use it. We can essentially simulate a user’s behaviour in our application (like a bot), and test for what is happening in the application. An E2E test might look something like this, for example:

  1. Go to the index page
  2. Click on the menu icon
  3. Click on the ‘Products’ page
  4. Expect the ‘Featured’ product category to be displayed

To create E2E tests we will be using a new tool called Protractor which will allow us to simulate user behaviour in this way, and it will run our tests for us automatically through a browser (you can actually see the actions being performed in real time, which is pretty cool) by using a Selenium server. We will need to install the webdriver-manager for the Selenium server but you don’t really need to know anything about Selenium in order to conduct your E2E tests. We still use Jasmine with Protractor in order to define our E2E tests.

1. Generate a new Ionic 2 Application

We’re going to start off by generating a new Ionic 2 application. Instead of using the blank template, which we would typically use, we are going to use the default tabs template so that we have something a little more interesting to test with Protractor.

Run the following command to generate a new Ionic 2 application:

ionic start ionic2-e2e-example

Once generate, make it your working directory by running the following command:

cd ionic2-e2e-example

2. Set up Protractor

Now that we have our project generated, we are going to set up Protractor. We will need to install some dependencies and also set up some configuration files.

UPDATE: If you would prefer, you can follow the setup instructions in this post instead. The instructions in this post use a pre-existing repository that already has unit tests and E2E tests configured, so you don’t need to go through the lengthy setup process described below. If you do use this easier approach, you just need to make sure that you run your tests with the following command:

npm run e2e

instead of with:

protractor

NOTE: A lot of the configuration for this setup is based on this repository by Lathonez. The configuration in this tutorial is just a more simplified version of that repository, with just the bare bones of what we will need to get protractor running, and all of the unit testing and other things stripped out. I’d like to make it clear that E2E testing isn’t something to be used instead of unit testing, they should be used together. However, for demonstration purposes, I want to show you how to set up and use Protractor in isolation.

Run the following command to install Protractor

npm install protractor --save-dev

Run the following commands to install and update the webdriver:

npm install -g webdriver-manager
webdriver-manager update

As I mentioned before, Protractor uses a Selenium server to run the E2E tests, which is why we need to run these commands.

Install the dependencies for Protractor with the following commands:

npm install jasmine --save-dev
npm install jasmine-spec-reporter --save-dev
npm install ts-node --save-dev
npm install connect --save-dev

npm install @types/jasmine --save-dev
npm install @types/node --save-dev

NOTE: There is an incompatibility between the current TypeScript version in Ionic 2 projects and the Jasmine types. In order to work around this, for now, you should ensure that the follow version of the Jasmine types is used:

"@types/jasmine": "2.5.41",

Just update your package.json file with that version and run npm install if you run into any issues with Jasmine.

Create a file at in the root folder of your project called protractor.conf.js and add the following:

var SpecReporter = require('jasmine-spec-reporter').SpecReporter;

exports.config = {
    allScriptsTimeout: 11000,
    directConnect: true,
    capabilities: {
        'browserName': 'chrome'
    },
    framework: 'jasmine',
    jasmineNodeOpts: {
        showColors: true,
        defaultTimeoutInterval: 30000,
        print: function() {}
    },
    specs: ['./e2e/**/*.e2e-spec.ts'],
    baseUrl: 'http://localhost:8100',
    useAllAngular2AppRoots: true,
    beforeLaunch: function() {

        require('ts-node').register({
            project: 'e2e'
        });

        require('connect')().use(require('serve-static')('www')).listen(8100);

    },
    onPrepare: function() {
        jasmine.getEnv().addReporter(new SpecReporter());
    }
}

We are going to create a folder to hold all of our E2E tests, as well as some configuration for those tests.

Create a folder called e2e in the root folder of your project

Create a file called tsconfig.json inside of the e2e folder and add the following:

{
  "compilerOptions": {
    "sourceMap": true,
    "declaration": false,
    "moduleResolution": "node",
    "emitDecoratorMetadata": true,
    "experimentalDecorators": true,
    "lib": [
      "es2016"
    ],
    "outDir": "../dist/out-tsc-e2e",
    "module": "commonjs",
    "target": "es6",
    "types":[
      "jasmine",
      "node"
    ]
  }
}

Now all you have to do to run your tests with Protractor is to run the following command inside of your project:

protractor

Of course, we haven’t defined any E2E tests yet so we don’t have anything to run just yet.

3. Create an E2E Test

As I mentioned, we are just going to create a simple test to see if things are working. I will also show you some of the basic methods of triggering user interactions and testing with Protractor.

NOTE: The following test is not an example of best practice, it is just a simple dummy test for demonstration purposes.

Create a file at e2e/test.e2e-spec.ts and add the following:

import { browser, element, by, ElementFinder } from 'protractor';

describe('Example E2E Test', () => {

  beforeEach(() => {
    browser.get('');
  });

  it('the home tab is displayed by default', () => {

      expect(element(by.css('[aria-selected=true] .tab-button-text')) // Grab the title of the selected tab
        .getAttribute('innerHTML')) // Get the text content
        .toContain('Home'); // Check if it contains the text "Home"

  });

  it('the user can browse to the contact tab and view the ionic twitter handle', () => {

    // Click the 'About' tab
    element(by.css('[aria-controls=tabpanel-t0-2]')).click().then(() => { 

      // Wait for the page transition
      browser.driver.sleep(1000);

      expect(element(by.css('ion-list ion-item ion-label')) // Grab the label of the list item
        .getAttribute('innerHTML')) // Get the text content
        .toContain('@ionicframework'); // Check if it contains the text "@ionicframework"

    });

  });

});

This E2E test is quite similar to a normal unit test that we create with Jasmine, except for a few key differences.

One thing you might notice if you are already familiar with creating unit tests, is that we aren’t using TestBed and we don’t have to import or set up any components in the test. We are just using the application, so we point the browser to the root of the application and start interacting with it.

We are also using a few imports from the protractor library. The browser will allow us to control the browser like by directing it to certain URLs, or asking it to wait a certain amount of time before continuing the test. The element, by and ElementFinder imports allow us to find elements on the page and interact with them (e.g. find a button and then click on it).

In the test above, we have created two tests, and before each of the tests we redirect the browser back to the initial page with browser.get('').

The first test checks that the home tab is displayed by default. To do that it first uses a CSS attribute to grab the appropriate element, which is the tab button for the currently selected tab. You can use any normal CSS selector with by.css, and since the currently selected tab in an Ionic 2 application will have its aria-selected attribute set to true we use that to grab the currently selected tab. We then check its innerHTML to see if it is indeed the ‘Home’ tab (in which case, it would contain the text Home).

The next test is similar, except this time we grab a different tab and trigger a click on it to test that the user can browse to that tab. We tell the browser to wait for 1 second whilst the page transition occurs (which is likely longer than needed), and then we check that the <ion-label> element contains the Ionic Twitter handle.

If you run these tests with the following command:

protractor

you should see something like this:

Ionic 2 E2E Test

Summary

This is a reasonably basic introduction to Protractor and E2E tests in general, but hopefully, it helps explain some of the key concepts. Getting automated tests set up can often be quite tricky, but I think the set up for Protractor is actually pleasantly straight-forward.

I will cover End-to-End testing more in future tutorials, including how to better structure your E2E tests and how you can incorporate E2E tests into a Test Driven Development approach.

What to watch next...