Javascript Tutorials

Getting Started with Angular 2 End To End Testing

October 12th, 2016 | By Lamin Sanneh | 10 min read

There have been many reasons I have held out on adding automated testing to my applications in the past. One of them was not knowing the benefit vs cost ratio. Another is the thought that they would be hard to integrate into existing production applications.

How do we go about testing our applications without rewriting them from scratch just to introduce testing into them?

Let’s start out by briefly identifying the types of tests out there. There are many types of application tests but the two most common ones are unit tests and end to end tests (also known as integration tests). Unit testing is a type of testing which tests the behavior of your code itself. It has nothing to do with what the user sees but makes sure your methods do what they were intended to do. Integration testing is a testing type that mimics what the user intended for the applications to do. So for example, it automates the process of logging into a system, creating posts, logging out. This all happens automatically and you can see it happen visually.

These two types of tests are usually used in conjunction with each other. For new applications, that would be ideal. If the case is that time is limited or you inherited an existing application, end to end testing may be more suitable than unit tests. This is because we do not have to rely on deep knowledge of the existing codebase at first. We can also cover more scenarios quicker than unit tests as they do not test single units but scenarios.

Unit tests are still important but I believe if you had to choose one to start with, end to end testing is a better choice. In this article, we will be testing an existing Angular 2 todo application. We will be using integration tests and will cover several scenarios.

If you need familiarity on getting started with Angular 2, have a look at this other Angular 2 article from Jscrambler.

Scenarios to be tested

  • When the application initially loads, we should have three todos on the list.

  • We should be able to add a new todo.

  • We should be able to click on a todo and be taken to the details page of that todo.

  • We should be able to delete a todo.

  • We should be able to edit a todo title and see the new title reflect on the homepage in the list of todos, after saving the todo.

  • We shouldn’t be allowed to save an empty todo and the list of todos should still be the same length after clicking on disabled save button

  • Initially, the add new todo button should be disabled when we load the homepage

  • The save todo button should only be enabled only when we start typing a todo title

Todo Application Outline

Let’s describe our todo application briefly. The application will initially list out a list of todos on the homepage. Three to be precise. The data will not come from a server but instead will be loaded from a hardcoded fixture file.

On the homepage, we can add new todos. We can also visit a todo details page by clicking on its title. On this page, we can edit the todo title or delete the todo.

Clone and Setup the Todo Application

  • Clone the non-tested application I have pushed to the repository here.

    Make sure you are on the master branch. Next, you need to have several tools installed to be able to follow along. As of this tutorial, Angular 2 has come out of Release Candidate and is at version 2.

  • Make sure you have NodeJS version Node 4.x.x or above installed.

  • Install the node dependencies using the command

npm install

    while inside the cloned repository

    Install the Angular-CLI globally using

npm install - g angular - cli@ latest
  • Angular 2 end to end tests are run using a tool called protractor Install protractor globally using

npm install - g protractor
  • When all dependencies are installed, start the development server using

ng serve

    and navigate to the browser url http://localhost:4200 where you shall see three todos listed.

If you are having an issue starting the server, you may like to reference this stackoverflow issue on fixing the issue.

Important Angular 2 Testing Concepts

End to end tests are located in the folder e2e. There is already a sample test file there called es2/app.e2e-spec.ts.

The tests in there are written using jasmine framework. There are many ways to modularize and organize Angular 2 end to end tests but for simplicity’s sake, we will put all the tests for this article in this one file.

Our application only has one concern which is todos. For curiosity’s sake and for those who want something more complex than the above, let’s imagine a scenario where this was a more complex application which for has other concerns like orders, userProfile. A way I would handle tests for that application is to create a folder inside of e2e for each of those concerns and put the respective tests in each folder.

In this case we will have two folders named e2e/orders and e2e/userProfile. We can have just one test file in each folder or more than one to cater for subsections of each of those concerns if we needed to. The only thing to be mindful of is that each of the files need to end with the word e2e-spec.ts so that the protractor test runner can pick up the test files.

Ok, so back to our simple, one-file test. If you look in the file, you will see an import statement at the top. That import is where we put common functions which will be used by several tests. In this article, however, we won’t be using it. Think of it as a library of functions.

After the import statement, we have a describe block which has two other function calls namely beforeEach and it inside its callback.

The callback passed to beforeEach is called for every test inside the describe block. Leave it as it is.

Individual tests are put inside of the callback passed to the it function.

Let’s run the current test using the command

protractor

If you have an issue running protractor, run either one of these two commands as referenced from here

. / node_modules / protractor / bin / webdriver - manager update

or

webdriver - manager update

The current test should fail as it expects to see the text “app works” on the homepage. This is not the case as we have modified the contents of the homepage.

Before we start writing our own tests, let’s understand some of the most common functions we will be using in writing Angular 2 end to end tests.

Navigating to pages

Inside the test files, there is a global variable called browser available. It is imported using the import statement

import {
browser, element, by
}
from 'protractor/globals';

which you can add there now.

We use this to navigate to any page available in our application by writing for example

browser.get('/');

to go to the homepage and

browser.get('/users');

to go to the users page. Note that these URLs are relative but we can use absolute URLs as well but I recommend to use relative URLs as they are more maintainable in case your root URL changes.

Selecting elements

It is common practice to select elements on the current page. There is a global variable called element which you can use to select elements. It accepts a locator which can be created using the global called by

An example to select the p tag with the class of green is done using

let greenParagraph = element(by.css('p.green'));

To select many elements you should use the slight variation

let greenParagraphs = element.all(by.css('p.green'));

This will give an array as opposed to a single element.

Protect your Angular App with Jscrambler

Grabbing Element Text

To get the text of an element, you have to select it first as above and then call the getText function on the result like so.

let greenParagraph = element(by.css('p.green'));
let text = greenParagraph.getText();

Clicking Elements

Clicking elements can be done using the syntax below

let submitButton = element(by("form .submit-button"));
submitButton.click();

Counting Elements

We can also count elements using the syntax below.

let blueParagraphsList = elements.all(by("p.blue"));
let count = blueParagraphsList.count();

Test Scenarios

With the concepts out of the way, let’s now cover the scenarios we listed out above

Confirm that we have three Todos initially

When the application initially loads, we should have three todos in the list

Inside the test file e2e/app.e2e-spec.ts, delete the call to the it function below the beforeEach block and add there

it("should show three todos when we first load the todo app", () => {
browser.get("/");
let todos = element.all(by.css(".todos .todo"));
expect(todos.count()).toEqual(3);
})

Don’t forget to add this import statement at the top of this file

import {
browser, element, by
}
from 'protractor/globals';

Now when you run the protractor command, another browser windows will open up and close quickly and in your console you should see a passing test in green.

Hurray! We have just written our first passing Angular 2 end to end test.

Add a new Todo

Now onto the next one. We should be able to add a new todo. Let’s add another it test block using the following code

it("should be able to add a new todo", () => {
browser.get("/");
let newTodoInput = element(
by.css(".add-todo input[type=text]"));
newTodoInput.sendKeys("Todo 4");

let newTodoSubmitButton = element(
by.css(".add-todo input[type=submit]"));
newTodoSubmitButton.click();

let todos = element.all(by.css(".todos .todo"));
expect(todos.count()).toEqual(4);
})

What we’ve done here is enter text into the new todo input box and then submitted the form. Then we checked if we now have four todos. If that is the case, then our test has passed.

We just introduced another function called sendKeys. This can be called on a selected element. It is used to enter text into elements like input fields.

View Todo Details page

We should be able to click on a todo and go to the details page of that todo. Let’s do that using the following test.

it("should be able to click on a todo on the homepage and get to 
the details page", () => {
browser.get("/");
let firstTodo = element.all(by.css(".todos .todo")).first();
let firstTodoText = firstTodo.getText();

firstTodo.click();
let inputFieldText = element(
by.css("todo input[type=text]")).getAttribute("value");

expect(inputFieldText).toEqual(firstTodoText);
})

Delete a Todo

We should be able to delete a todo. Now let’s try to delete a todo and see if we are successful.

We will go a todo page and click on the delete todo link. When we return to the homepage, we should have one less todo.

it("should be able to delete a todo", () => {
browser.get("/");
let firstTodo = element.all(by.css(".todos .todo")).first();

firstTodo.click();

let deleteLink = element(by.cssContainingText("span", "Delete"));
deleteLink.click();

let todosList = element.all(by.css(".todos .todo"));

expect(todosList.count()).toEqual(2);
})

Edit a Todo title

We should be able to edit a todo title and see the new title reflect on the homepage in the list of todos, after saving the todo

it("should be able to edit a todo title", () => {
browser.get("/");
let firstTodo = element.all(by.css(".todos .todo")).first();

firstTodo.click();

let todoInputField = element(by.css("todo input[type=text]"));
todoInputField.clear();
todoInputField.sendKeys("Editted Todo1 Title");

let saveButton = element(by.css("todo button[type=submit]"));
saveButton.click();

firstTodo = element.all(by.css(".todos .todo")).first();
let firstTodoText = firstTodo.getText();

expect(firstTodoText).toEqual("Editted Todo1 Title");
})

Cannot save empty Todo

When we want to save an empty todo, we shouldn’t be allowed to and the list of todos should still be the same length after clicking on the disabled button.

it("should not be able to save an empty todo", () => {
browser.get("/");
let newTodoInput = element(by.css(".add-todo input[type=text]"));

let newTodoSubmitButton = element(
by.css(".add-todo input[type=submit]"));
newTodoSubmitButton.click();

let todos = element.all(by.css(".todos .todo"));
expect(todos.count()).toEqual(3);
})

Save Todo button is disabled initially

Initially, the add new todo button should be disabled so add these lines of code

it("should have add todo button be disabled initially", () => {
browser.get("/");
let newTodoSubmitButton = element(
by.css(".add-todo input[type=submit]"));

expect(newTodoSubmitButton.isEnabled()).toEqual(false);
})

Enable save Todo button when we start typing

The save todo button should only be enabled only when we start typing a todo title.

it("should only enable save todo button when we start typing
a new todo title", () => {
browser.get("/");
let newTodoSubmitButton = element(
by.css(".add-todo input[type=submit]"));
let newTodoInputField = element(by.css(".add-todo input[type=text]"));

newTodoInputField.sendKeys("New Todo 4");

expect(newTodoSubmitButton.isEnabled()).toEqual(true);
})

Conclusion

That brings us to the end of this Angular 2 end to end testing article. End to end tests can be written quickly without any knowledge of the codebase. It is a quick way of making sure that any new changed you make to the codebase, which could potentially introduce bugs, will be caught quickly.

We did introduce other additional methods to the ones we talked about in the concepts section. You can explore them at the Protractor API’s website located here. You can find the completed and tested version of this application at this github repository.

I hope this was an encouraging introduction to start testing your front-end applications even before you change a single line of the project’s code.

That’s it for now and please let us know your thoughts and experiences about testing in general or just about Javascript frameworks or Angular 2 if you will. Thanks for reading.

Lastly, if you want to learn how you can secure your Angular source code against theft and reverse-engineering, be sure to check our guide.

Jscrambler

The leader in client-side Web security. With Jscrambler, JavaScript applications become self-defensive and capable of detecting and blocking client-side attacks like Magecart.

View All Articles

Must read next

Javascript Web Development

Getting Started with Angular 2

Angular 2 has been built to be completely decoupled from the DOM, meaning that developers can use the framework to build more than just web applications.

November 10, 2016 | By Thomas Greco | 6 min read

Frameworks Javascript Tutorials

Angular 2 and Typescript Conference Browser Application

How to build a conference browsing application using typescript and the Angular 2 CLI

September 6, 2016 | By Jscrambler | 11 min read

Subscribe to Our Newsletter