Testing in Ionic 2

E2E (End-to-End) Testing in Ionic 2: Test Driven Development



·

In the past two tutorials on E2E testing in Ionic, I’ve introduced the concept of End-to-End testing and some ways that you can better structure your E2E tests.

If you are new to E2E testing you will likely understand at this point that an End-to-End test can test your application in a way that the user will actually use it. This might be things like:

  • The user can select a product and add it to their cart
  • The user can log in and change their account information
  • The user can navigate to the Checkout page

Which is different to a unit test which only tests a small isolated unit of code, which might be something more like the getData function should return an array of data.

But how do we decide what to test in an E2E test? Should we just create a bunch of random tests and run them throughout development? Should we only worry about E2E tests once the application is completed and we want to verify that it works under certain circumstances?

There’s no correct answer to that question, different people will have different opinions on how it should be done. I think it makes the most sense to do what makes sense to you and your team, and what you think will best test your application.

With that in mind, I’d like to show you how I integrate E2E tests into my workflow. If you’ve read my series on Test Driven Development in Ionic 2 then you may know that I am partial to the TDD approach. I’d recommend reading those posts if you are unfamiliar with the concept, but the general idea is to write tests for your code before you write the code itself. The tutorials I wrote were in the context of unit tests, but that approach can easily be expanded to include E2E tests.

Integrating E2E Tests into a TDD Approach

I’ll walk through the general process I use when using a TDD approach that includes E2E testing, and then I’ll walk through a specific example to help demonstrate the concept. The approach still uses the basic principle of writing tests before writing code, but there’s kind of an “extra layer” added in there (tests within tests, if you like). The E2E test will help define the unit tests, and by implementing the code to satisfy the unit tests we will eventually satisfy the original E2E test.

1. Write an E2E test and watch it fail

The first step is to start out with writing an E2E test and watching it fail, which is the same concept we used in the TDD series for unit testing except that it’s an E2E test instead of a unit test.

2. Write unit tests and watch them fail

In order to satisfy the E2E test, we need to write some code. But with a TDD approach, we should have tests for our code before we write the code (and the E2E test is a bit too broad to count). At this stage, you should write a unit test that will move you towards the goal of satisfying the functionality described in your E2E test.

3. Code until all unit tests are satisfied

Now you should implement the code to satisfy your unit test (or unit tests) until they all pass.

4. Check if the E2E test passes

Once all the unit tests pass, you should now go back and check your E2E test to see if it passes. If the E2E test is not yet satisfied, you should go back to Step 2 and write another unit test. Keep repeating this process until the E2E test passes.

5. Go back to Step 1

Now that your E2E test is passing, you should go back to Step 1 and write a new E2E test and repeat the process. Keeping repeating this process over and over until your application is complete.

Example

It’s much easier to make a process like this click in your brain by seeing an actual example. I’m currently working on an app where I’m using this approach, so I figured it would be fitting to walk through one of the E2E tests I implemented with this approach.

Step 1 – Write an E2E Test

One behaviour I wanted to implement in the application was for the user to be able to select a module from a list and view its lessons, so I wrote the following E2E test:

  it('a user can view the lesson list for a selected module', () => {

  // Select a module
  let moduleToTest = selectPage.getModuleElement();

  // Trigger a click to navigate to module
  moduleToTest.click();

  // Wait
  browser.driver.sleep(1000);

  // Test that there are now lessons displayed
  expect(modulePage.getLessonList().count()).toBeGreaterThan(0);

  });

Step 2 – Write Unit Tests

The E2E test assumed the existence of some things I hadn’t created yet, like the SelectModule page, so I generated that and created the following test:

    it('should display a list of modules once the page has loaded', fakeAsync(() => {

        comp.ionViewDidLoad();        
        tick();

        fixture.detectChanges();

        de = fixture.debugElement.query(By.css('ion-list'));

        expect(de.children.length).toBeGreaterThan(2);

    }));

This test assumed the existence of a data provider that had not been created yet, so I created a couple of unit tests for that:

  it('getModules should return a promise that resolves with an array', () => {
    moduleDataService.getModules().then((data) => {
    expect(Array.isArray(data)).toBeTruthy();
  });

  });

  it('the data getModules provides should contain more than one module', () => {
    moduleDataService.getModules().then((data) => {
      expect(data.length).toBeGreaterThan(1);
    });
  });

Step 3 – Code to Satisfy Unit Tests

With the above tests in place, I was able to implement my data provider, which then allowed me to implement the code for the SelectModule page. At this point, all the unit tests have passed.

Step 4 – Check if E2E Test Passes

With the unit tests passing, I was able to go back and check if the E2E test now passes. But it didn’t because the functionality to actually click a module and go to the module page had not been implemented yet. So, I went back and wrote more unit tests to test that functionality, implemented the code, got the unit tests to pass, and tried the E2E test again. This process was repeated until the E2E test finally passed.

Step 5 – Write another E2E Test

Once the previous E2E test was satisfied, I wrote my next E2E test, which was:

  it('a user can select a lesson from a module and view the lesson content', () => {

  let lessonToTest = modulePage.getLessonList().get(2);

  // Trigger a click to navigate to module
  lessonToTest.click();

  // Wait
  browser.driver.sleep(500);

  // Check if there is content
  expect(lessonPage.getContentArea().getText()).toBeTruthy();

  });

and went through the whole process again.

Summary

I find that this approach works quite well for me: it adheres to the principles of the test driven development approach, it provides a nice structure for building out the functionality of the application, and it will result in an application with great test coverage.

There’s not a whole lot of information out there right now in the way of best practices for testing Ionic 2 applications and Angular applications in general, so if you have your own thoughts please leave a comment below.

What to watch next...