Lesson 3

Testing Concepts

WARNING: This module is deprecated and no longer receives updates. Protractor is likely being removed as the default from Angular applications and Protractor itself will likely stop receiving updates and development in the future. I would recommend checking out the Test Driven Development with Cypress/Jest as a replacement.

Some key concepts to creating effective tests


Lesson Outline


IMPORTANT: This module is deprecated and is only kept for legacy reasons. It will not receive any more updates. As of May 2021 Protractor will no longer be included by default in new Angular projects, and the development of Protractor will eventually cease completely. Instead, you can use Cypress to test your Ionic/Angular projects.

The only reason that you should go through this module is if you need to maintain a legacy Angular application that is still using Protractor. If you want to learn how to use Test Driven Development with Ionic and Angular you should instead start the Cypress/Jest module also available in Elite Ionic.

Testing Concepts

Automated tests have been around for a long time, and there is a lot of literature on testing concepts and best practices. You could read entire books purely on testing concepts (I found this book particularly helpful). Although a specific language may be used to illustrate tests, the same concepts apply no matter what language you are using (you won't find any Javascript in the book I linked).

As important as understanding these concepts will become as you learn to test your applications, I don't want to get too bogged down in theory right away. I want to keep this module as practical as possible, but there are a few testing concepts that I think we do need to understand right out of the gate.

In this lesson, we will cover a few core testing concepts that are going to help us create and maintain our tests.

NOTE: I mentioned people generally have a lot of opinions about testing, and often there is no one "true" or "best" way to do it (although there are certainly justifications for some approaches being better than others). I am going to start mentioning things that you "should" be doing, but just keep in mind that this is also my own view on testing best practices. When I say that you should be doing something, it doesn't necessarily mean that is the one way that you have to do it. There might be special circumstances where it makes sense to break certain rules, or there might be a different approach in general that works better for you or your situation.

AAA: Arrange, Act, Assert

This is perhaps the most important concept to understand, it is the guiding principle for how we will create our tests. When creating a test, there are three distinct steps:

  1. Arrange
  2. Act
  3. Assert

It's actually quite simple to think about. First, we arrange our test by getting the test into the state it needs to be in to perform the test, then we act by executing some code, and then we assert that something specific has happened. To put it less formally:

  1. Set it up
  2. Do something
  3. Check that what you wanted to happen actually happened

Let's take a look at our example test we created with vanilla Javascript and see how this concept would apply to that:

// Test that `incrementTotal()` increases the total by 1

/* Arrange */
let myTestObject = new SomeObject();

/* Act */
let oldTotal = myTestObject.getTotal();

let newTotal = myTestObject.getTotal();

/* Assert */
if (newTotal === oldTotal + 1) {
  console.log('test passed!');
} else {
  console.log('test failed!');

First, we arrange the test by setting up the object we are testing, then we act by accessing some methods on that object, and then we assert that the newTotal should be equal to oldTotal + 1.

Our tests will get a little more complicated later, and we won't be writing them with vanilla Javascript like this, but the concept will remain the same: Arrange, Act, Assert.

One Assertion Per Test

When creating tests, you should generally only have one assertion per test - your test should only be testing one specific thing. This is not strictly enforced, you can easily have multiple assertions in a single test if you want, but in general, it's not a good idea.

Let's take a look at a test that would violate this rule (I will substitute the actual implementation with comments for now):

it('should allow todos to be modified', () => {
  // trigger edit todo functionality
  // expect that the edit page was launched with the todo
  // trigger delete todo functionality
  // expect that the todo was passed to the delete function in the data provider

NOTE: The test structure you see above is how we will structure tests throughout this module. I will introduce you to this concept in the next lesson, but I will use a couple of examples using this pseudo code structure throughout this lesson to illustrate some points.

We're testing if todos can be modified, but we're really testing two things here: can todos be edited, and can todos be deleted. The reason that this isn't a good structure is primarily that:


Thanks for checking out the preview of this lesson!

You do not have the appropriate membership to view the full lesson. If you would like full access to this module you can view membership options (or log in if you are already have an appropriate membership).