29
.
1
.
2019

How to add protractor tests to your application

Why are end-to-end tests so important?

Do I really need e2e tests for my application?

Let’s say we have written plenty of unit tests and also some integration tests covering some services. Would that be enough? The short answer is always “no”. You can have your application secured with as many unit tests as you want. You may have the confidence that your application is bulletproof with integration tests but in the end your web application must be loaded in a browser and there are a lot of layers that must match together when the client is calling the application that your backend is serving. Many things can fail, that’s why you should add some end to end tests to your application covering at least your application’s most important flows, emulating a user behaviour. Because at the end of the day what we want to ensure is that our clients are able to use the features we built for them.

Protractor introduction

Protractor is a node framework aimed for testing web applications. It is developed by Google and it is optimized for testing AngularJS and Angular applications, however it can fully work with other non-angular applications. Protractor is built on top of WebDriverJS which is based on promises.

Web-driver architecture

Protractor will execute the test scripts communicating through WebDriver API via http with Selenium Server that could be started embedded or standalone. The selenium server will then translate the instructions send by Protractor script and then will send these translated instructions to the browser driver though a JSON Protocol in a language the browser can understand.

Web driver architecture

For more info about how works the communication between protractor and web driver you can go here.

Locators

In order to be able to select DOM elements, Protractor makes use of Locators. These locators are exposed through the global by object. There are some locators that are added by Protractor itself and there are also some that are just extended from webdriver.By Object. For example:

Protractor locators

shell:by.model('item.name')
shell:by.repeater('items')
shell:by.options('options')

Locators inherited from webdriver.By

shell:by.id('idName')
shell:by.className('className')
shell:by.css('div[class=className]')

For more info about Protractor.By Object you can go here.

Element function

The locators are passed to the element function, as below:

shell:element(by.css('some-css'));

Actions

The element() function returns an ElementFinder object. The ElementFinder uses the locator you passed in order to locate the DOM element, but it has not actually done so yet. It will not call the browser until an action method has been called.

github:cefc4c1d33d088278cba1926a9af1c4f

Custom locators

Protractor gives us the possibility to add some custom locators. You can use the addLocator method from by object. You can just add the locator in you protractor config file:

github:f584feb13ae63942f223771462d106d6

Then you add this html attribute on every element you want to locate. The value of this attribute will be used to locate this element:

github:39bc9534eaab6fb630a7f9f2581cde2d

And lastly you can locate these elements in your page object by using your custom locators:

github:b2f232bef3a721bab5f3fd9c6896a315

Test Ids

In our project we established as good practice to use always testIds through custom locator to locate the elements we want to find. This locator will be based on an attribute of the html element. The reason behind the idea of avoiding normal locators (className, id, …) is that the name of the class could change because of many reasons and that would break our tests. If we use this custom attribute for locating elements, every developer in team is aware that this attribute is used exclusively for test purposes and that this can not be changed or deleted without adapting the tests.

Minimal setup

The minimal version for a protractor configuration consists of only two files:

github:7a29329ffe466b92970c0bdf040ae570
github:1297e51b1e3917bcea5796bb9519b021

We execute our test:

Login spec running

Running the tests

Step 1: Install the protractor executable:

shell:npm install -g protractor

Step 2: Install the webdriver package:

shell:npm install -g webdriver-manager

Step 3: Start the webdriver:

shell:webdriver-manager start

Step 4: Run the protractor executable and as parameter we will pass the protractor.conf.js file:

shell:protractor protractor.conf.js

Step 5: Optionally we could indicate the suite we want to run (if no suite is passed, all tests will run):

shell:protractor protractor.conf.js --suite=basic

Note: Steps 1-3 could be skipped if we have protractor and webdriver-manager installed locally through a package.json file.

github:3e8df4f203eeea326e65a30e575899af

We can see that we have the protractor package listed in the dependencies section. Also, a couple of npm scripts were added in order to build protractor project (download protractor package and webdrivers) or/and running the tests. For example for building and running the tests we could just run:

shell:npm run build-and-test

Page Objects

If you have to write e2e-tests is always a good idea to follow the Page Object pattern. The idea behind that is that for every page (or section) we create a file where we define all the elements from the page itself and, as well, we can create methods that perform the possible activities on the page. In this way we can import the page object in the spec file and call these page object methods, enhancing the readability of the tests, favouring the DRY principle and also making the code more maintainable. If we change a element or something related to a page we only have to adapt the change in the page object.

A page object look like this:

github:9008b7d9a4450b33ba87d833f1508842

And then the spec file is much simpler and much more readable:

github:6dcd6d5607a3bb5943b3004976a8c53b

Reporting

Like said before end-to-end tests have a vital importance for ensuring your applications functionality. However they have a drawback: they are flaky. Since in these tests we are testing all of the application’s layers, every infrastructure problem or timing issue could lead to an error. Therefore these kind of tests tend to give false positives very often.

Because of this inherent flakiness it is extremely important to have tools that ease the analysis of possibly failed builds. There are many report plugins for protractor, but in our project we use the library protractor-jasmine2-html-reporter. When we use this library a screenshot will be generated at the end of every spec (very handy for analyzing errors) and at the end of the execution also a html site will be generated, showing the info from the run specs and their corresponding screenshots:

Html report for tests

In order to enable we will have to add the package in our package.json file and import the Reporter in our protractor.conf.js file. There you could configure the path where the screenshots and the html are going to be saved:

github:cc7d6970810f62566cd3fdb2df4ce758

Test examples

You can find the test examples from this blogpost in this repository.

References

Julio
Software Engineer - Java, JVM

Leidenschaft, Freundschaft, Ehrlichkeit, Neugier. Du fühlst Dich angesprochen? Dann brauchen wir genau Dich.

Bewirb dich jetzt!