End to end testing is a somewhat polarized subject in software engineering.

While, some argue against it with valid points, we still think they are an important tool to have under our belt. In this article, we will go over a few of the other testing ways, and dig deeper into the why, and how of end to end testing for Angular 2 applications.

Before we dig deep if you aren't familiar with the different levels of testing, here's a quick overview.

1. Unit Tests

This is the smallest possible unit of testing where we focus on thoroughly testing a small part of your application. Unit tests cover one small unit and don't bother about how multiple units work together.

We use unit tests in the application because they are fast, reliable and point us in the exact direction of the bug.

2. Integration Tests

These tests cover multiple units. The goal here is to test 2 or more small components of the application and ensure that they integrate as expected.

Integration tests point out important bugs that would show up when multiple components run together in the application.

3. End to End Tests

The tests above usually test code directly. They don't test how the code would perform with hundreds of different environment variables. They don't know how a user would run the application.

That's why we use end to end tests. These end to end tests simulate a production like environment. It's as simple as testing your application from start to finish - just like a user.

We can run end to end tests on multiple browsers and find bugs that emerge in certain environments / browsers - such as... Internet Explorer.

4. Manual Testing

Finally, we test the application manually to look for any unexpected behavior that didn't show up in the tests above.

End to end Testing in Your Organization

a. What's the Purpose of Testing

Sometimes we tend to forget why we write software. Is it for the fun of building great products - of course! But who really benefits from these products?

The Users!

Every step we take to improve the process of writing and shipping great software should keep the user in mind. But then why do we write tests? The user doesn't even get to see them!

We write tests to catch bugs before a user finds them lurking around the application. Remember that as you read further...

b. Why use end to end Testing?

As the application continues to grow, testing the website manually each time you write a new component is not feasible.

That's why we need to automate the testing process as much as possible using end to end testing. We can then see how different parts of the application - such as client-side code, the database queries, API controllers, etc. run together.

c. But Wait! End to end Testing Can Be Harmful Too!

Even though end to end testing sounds so magical and surreal, there are downsides to this testing strategy, especially when it comes to practices like continuous delivery.

Let's get back to thinking about the user. What do you do when an end to end test fails?

In this scenario, the only thing we know is that there is a bug. We don't know which part of application caused the bug. We can't isolate the function or module that created this problem.

Your user only cares about the bug being fixed. He does not care about whether you found the bug or not.

d. How Much of Your Application Should You Cover with End to End Tests?

We know by now that end to end tests only reveal the bug. We still need to find the bug and fix it. And that is the limitation of end to end tests. Isolating bugs is much easier with unit tests. We can cover all the small parts of the application with these, and we can cover the integration with other parts of the application with integration tests.

This would make it very difficult for any end to end tests to fail.

So even though end to end tests are important, we need to spend a considerable amount of time focusing on unit and integration tests.

Using a 70:20:10 ratio for the unit, integration and end to end tests in your application.

Tools for end to end Testing

1. Selenium

Selenium allows us to automate browsers - Chrome, Firefox, Opera and yes, even Internet Explorer!

But we don't need to use selenium directly. The Angular team gives a great tool that wraps around Selenium - Protractor.

2. Protractor

Protractor is the testing framework we'll be using for our Angular 2 applications.

Using protractor is as simple as installing the command line tools, creating the configuration file and running the tests.

3. Jasmine

Jasmine is the framework we'll be using for writing tests. You can read more about it here.

Your First Angular 2 Test Project

a. Run the App

Start off by cloning the git repository that accompanies this tutorial

git clone https://github.com/cartab/ng2-e2e-testing-tutorial

and

cd

into it.

Next, run the following commands: (you may need to use sudo ):

npm install typings install

Once we've got everything setup, let's start off the project with

npm run start

just to make sure that everything is running perfectly. This command should compile the typescript files and spin up a server on http://localhost:3000.

b. Installation

Now stop playing around with Jarvis! It's time to install the testing tools we spoke about earlier; we install protractor using:

npm install -g protractor

This installs two command line tools – protractor and webdriver-manager – a helper tool to quickly get an instance of Selenium Server running.

Finally, we run

webdriver-manager update

to download / update to the latest version of Selenium and chromedriver.

c. Running Tests for the First Time

We need two things to run the tests - a server running our application and an instance of a webdriver through protractor.

Use the npm run pretest command to start a simple server on http://localhost:8080.

Once the server is up and running, you can run protractor protractor.config.js . Now sit back and watch protractor fire up an instance of chromedriver and run your tests.

d. protractor.config.json

So what's the configuration magic that makes the testing possible?

exports.config = { // todo configuration magic };

Let's break down this configuration file. We need to tell protractor a couple of things -

1. How to connect to the web driver

directConnect: true,

This tells Protractor to directly connect to the webdriver (instead of connecting to a local Selenium server).

2. How to set up the browsers

capabilities: { 'browserName': 'chrome' },

Simply put, the capabilities option tells protractor which browser to use. We can use multiple browsers by using the multiCapabilities option.

3. The test framework we want to use

framework: 'jasmine',

We prefer using jasmine as the framework for writing tests, but you could choose any framework you like. Protractor allows you to define Custom Frameworks as well.

4. What tests to run

specs: ['./e2e/**/*.e2e-spec.js'],

These are the specs that we want protractor to run. In this case, we'll be running all the test that we have in the e2e directory and the .e2e-spec.js suffix and extension.

Note that we aren't mentioning the .ts (Typescript) files even though we write the tests using Typescript. That's why we need to run the Typescript compiler tsc before we run these tests.

5. Any global test information

baseUrl: 'http://localhost:8080',

The baseUrl is the URL the webdriver will visit by default.

Protractor has many more configuration options available. You can have a look at the entire list here.

e. The Spec File

Now all we need is the actual spec file to write out these tests. Let's quickly go over the basic tests in this application.

As usual, we start with defining the suite and any setup we need before running each test.

describe('Jarvis caller', function () { var callButton; beforeEach(function () { browser.get(''); // Gets the button to call Jarvis callButton = element(by.tagName('button')); }); });

Here, we're using browser.get('') to get the baseUrl that we defined in the config file - so the page gets refreshed before each test.

Let's just add a simple test first that checks if everything is good so far by checking the title of the page.

it('should display correct title', function () { expect(browser.getTitle()).toEqual('Angular 2 end to end Testing'); });

Next, we'll run a test to check if the UI has been set up properly and everything is displayed as per our expectations.

it('should see Jarvis after calling him', function() { // Click the button callButton.click(); expect(element(by.id('jarvis-response')).isDisplayed()).toBeTruthy(); // Checking if element is displayed expect(element(by.id('jarvis-response')).getText()).toEqual('Yes sir!'); // Check if display text is correct });

First, we click the Toggle Jarvis button to call Jarvis (display some text); then, we check if the new element is displayed and finally, we see if Jarvis responded as he should have with a Yes sir!

This a simple application with simple tests. As applications get more complex, you can use Protractor to fill in forms, navigate to different links and ensure that the browser behaves as it should.

As your tests start getting larger, you can split them by page or even by feature and use multiple spec files to keep the tests well organized.

Concluding Notes

Remember that we write tests only so that we can find bugs faster and improve the user's experience.

Focus on writing solid unit tests and integration tests; use end to end tests to find those pesky environment / browser specific bugs.