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
Development is done using Angular-CLI.
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.
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 ArticlesMust read next
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
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
