Angular, Protractor, and Cucumber

Proof-of-concept for an end-to-end-testing workflow

Originally tailor-made for AngularJS — the version 1 of Angular — Protractor is a tool for browser automation and end-to-end testing. It is, however, not limited to testing just Angular applications since the Protractor API is bascially an addition on top of selenium-webdriver. Protractor brings some specific methods and utilities to work with an Angular app running in the web browser. In general, you can select and inspect any DOM element on the page with so-called locators — e.g., locate an element by CSS selector or by ID — and trigger keyboard, mouse and also touch events. So in theory and practice, you could test any web app — whether it’s written in React, Vue, or any other framework — as long as it runs in a web browser.

Screenshot of a Chrome browser being remotely controlled by protractor / selenium-webdriver

On the other hand, cucumber is a behaviour-driven testing framework where you express features and scenarios in almost “natural language” syntax. In cucumber jargon, a feature describing the functionality of an app is composed of several scenarios. Each scenario consists of a series of steps that are expressed in Given, When, Then sentences. These sentences are then turned into a series of testing instructions — under the hood, it is all regular expression pattern matching — and executed by the test runner. Here’s an example of a cucumber scenario and it’s step definitions:

https://gist.github.com/dherges/fa665c6f53ffae7afc9cff36eadbe90d

The cool thing now is that cucumber can be plugged-in into protractor w/ a protractor-cucumber-framework. We can also write our testing code in TypeScript which fits well into the Angular ecosystem. The remainder of the story briefly talks through how to set up the tools. After we have initialized a fresh node.js project with a package.json in the current directory, we need to add the testing tools to it:

$ yarn add --dev protractor protractor-cucumber-framework cucumber typescript ts-node chai @types/chai @types/cucumber

Then, we configure protractor to use the cucumber framework and to compile sources with TypeScript:

https://gist.github.com/dherges/2c81bee554abd20169c014e1d5e60f71

Add a script to the package.json and we can then launch protractor:

"scripts": {
"pree2e": "webdriver-manager update",
"e2e": "protractor"
}

Give the base URL which the browser will initially load and then execute the tests against that web page:

$ yarn e2e -- --baseUrl https://angular.io

To complete this introduction, here is the testing code for the feature file that was demonstrated in the beginning. The code leverages the page object pattern. Interactions with the DOM, i.e. selecting node and triggering events, is encapsulated in a class that reflects the currently visible viewport in the browser. In this example, AppPage locates DOM elements and enters text into an input field (which triggers the search):

https://gist.github.com/dherges/8e98ac9925d38f2ab1442b862a562fdf

Now, the step definitions file wires up the cucumber steps with the AppPage object, turning the natural language syntax into a set of instructions for the browser automation. The good thing about the page object pattern is that changes to the application’s DOM structure don’t require us to refactor the cucumber code. Say, the selector for the search input changes, then we refactor the AppPage class but don’t need to touch our search.steps.ts file nor to change any of the Given, When, Then sentences expressed in the feature file!

Another cool thing is that we get test reports that we can present to our quality managers, product owners, bosses, friends, parents, or even grand-parents:

Feature: Search
  Scenario: Type in a search-term
√ Given I am on the angular.io site
√ When I type "foo" into the search input field

√ Then I should see some results in the search overlay
1 scenario (1 passed)
2 steps (2 passed)
0m01.151s

In a follow up story, I’d like to write specifically on testing an Angular application and how to mock HTTP / JSON APIs in the testing workflow!

As usual, you can explore the source code of this story’s demo project on GitHub: