Feature-driven end-to-end testing.
Demonstrates testing of an Angular app with Protractor and Cucumber.
$ yarn e2e -- --baseUrl https://angular.io
Protractor is used as the primary testing framework. It launches a web browser and allows remote control of the web browser.
Then, cucumber is plugged-in to protractor and TypeScript is added as onPrepare
hook.
Test specifications are written in .feature
files,
expressing each Scenario
with the Given
, When
, Then
keywords.
Feature: Search
As a developer using Angular
I need to look-up classes and guidelines
So that I can concentrate on building awesome applications
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
Scenarios are turned into automated browser actions in so-called step definitions. Step definitions are written in TypeScript and use protractor's API to control the browser. Basically, protractor's API is an addition on top of selenium-webdriver with specialized methods for handling Angular apps. When not using the Angular-specific additions, it is however to possible to test any other web app running in the browser.
The actual step definition is:
import { expect } from 'chai';
import { defineSupportCode } from 'cucumber';
import { AppPage } from './app.po';
defineSupportCode(({Given, When, Then, Before}) => {
let app: AppPage;
Before(() => {
app = new AppPage();
});
Given('I am on the angular.io site',
() => app.navigateTo());
When('I type "{term}" into the search input field',
(text: string) => app.enterSearchInput(text));
Then('I should see some results in the search overlay',
() => app.getSearchResultItems()
.then(elems => {
expect(elems.length).to.be.greaterThan(0);
}));
});
For test assertions, chai's expect/should API is a good fit with cucumber.
A Then
clause expresses expected behaviour or an expected outcome of an operation,
so a call to expect()
is a fluent way of writing down such an expectation.
Rather than automating the browser right from the step definitions,
the testing code uses the Page Object pattern.
The advantage here is that CSS selectors or the DOM structure of the application may change.
If we were scripting the browser straight out of the step definitions,
we'd need to update every scenario and every step definition.
With page objects, when the application changes,
just the AppPage
class needs to be updated to reflect the web app in a proper way:
import { browser, by, element, until } from 'protractor';
export class AppPage {
public navigateTo() {
return browser.get('/');
}
public enterSearchInput(text: string) {
return element(by.css('input[aria-label="search"]'))
.sendKeys(text);
}
public getSearchResultItems() {
const condition = until.elementsLocated(by.css('.search-results .search-result-item'));
return browser.wait(condition, 5000);
}
}
Top 5 Cucumber best practices, The Codeship:
Protractor styleguide – Carmen Popoviciu and Andres Dominguez, AngularConnect 2015:
Debugging Protractor (pausing a browser, taking snapshots, and more...)
Set Up:
$ yarn add --dev protractor protractor-cucumber-framework cucumber typescript ts-node chai @types/chai @types/cucumber
yarn add v0.27.5
info No lockfile found.
[1/4] Resolving packages...
[2/4] Fetching packages...
[3/4] Linking dependencies...
[4/4] Building fresh packages...
success Saved lockfile.
success Saved 181 new dependencies.
├─ @types/[email protected]
├─ @types/[email protected]
├─ @types/[email protected]
├─ @types/[email protected]
├─ @types/[email protected]
├─ [email protected]
├─ [email protected]
├─ [email protected]
├─ [email protected]
├─ [email protected]
├─ [email protected]
├─ [email protected]
├─ [email protected]
├─ [email protected]
├─ [email protected]
├─ [email protected]
├─ [email protected]
├─ [email protected]
├─ [email protected]
├─ [email protected]
├─ [email protected]
├─ [email protected]
├─ [email protected]
├─ [email protected]
├─ [email protected]
├─ [email protected]
├─ [email protected]
├─ [email protected]
├─ [email protected]
├─ [email protected]
├─ [email protected]
├─ [email protected]
├─ [email protected]
├─ [email protected]
├─ [email protected]
├─ [email protected]
├─ [email protected]
├─ [email protected]
├─ [email protected]
├─ [email protected]
├─ [email protected]
├─ [email protected]
├─ [email protected]
├─ [email protected]
├─ [email protected]
├─ [email protected]
├─ [email protected]
├─ [email protected]
├─ [email protected]
├─ [email protected]
├─ [email protected]
├─ [email protected]
├─ [email protected]
├─ [email protected]
├─ [email protected]
├─ [email protected]
├─ [email protected]
├─ [email protected]
├─ [email protected]
├─ [email protected]
├─ [email protected]
├─ [email protected]
├─ [email protected]
├─ [email protected]
├─ [email protected]
├─ [email protected]
├─ [email protected]
├─ [email protected]
├─ [email protected]
├─ [email protected]
├─ [email protected]
├─ [email protected]
├─ [email protected]
├─ [email protected]
├─ [email protected]
├─ [email protected]
├─ [email protected]
├─ [email protected]
├─ [email protected]
├─ [email protected]
├─ [email protected]
├─ [email protected]
├─ [email protected]
├─ [email protected]
├─ [email protected]
├─ [email protected]
├─ [email protected]
├─ [email protected]
├─ [email protected]
├─ [email protected]
├─ [email protected]
├─ [email protected]
├─ [email protected]
├─ [email protected]
├─ [email protected]
├─ [email protected]
├─ [email protected]
├─ [email protected]
├─ [email protected]
├─ [email protected]
├─ [email protected]
├─ [email protected]
├─ [email protected]
├─ [email protected]
├─ [email protected]
├─ [email protected]
├─ [email protected]
├─ [email protected]
├─ [email protected]
├─ [email protected]
├─ [email protected]
├─ [email protected]
├─ [email protected]
├─ [email protected]
├─ [email protected]
├─ [email protected]
├─ [email protected]
├─ [email protected]
├─ [email protected]
├─ [email protected]
├─ [email protected]
├─ [email protected]
├─ [email protected]
├─ [email protected]
├─ [email protected]
├─ [email protected]
├─ [email protected]
├─ [email protected]
├─ [email protected]
├─ [email protected]
├─ [email protected]
├─ [email protected]
├─ [email protected]
├─ [email protected]
├─ [email protected]
├─ [email protected]
├─ [email protected]
├─ [email protected]
├─ [email protected]
├─ [email protected]
├─ [email protected]
├─ [email protected]
├─ [email protected]
├─ [email protected]
├─ [email protected]
├─ [email protected]
├─ [email protected]
├─ [email protected]
├─ [email protected]
├─ [email protected]
├─ [email protected]
├─ [email protected]
├─ [email protected]
├─ [email protected]
├─ [email protected]
├─ [email protected]
├─ [email protected]
├─ [email protected]
├─ [email protected]
├─ [email protected]
├─ [email protected]
├─ [email protected]
├─ [email protected]
├─ [email protected]
├─ [email protected]
├─ [email protected]
├─ [email protected]
├─ [email protected]
├─ [email protected]
├─ [email protected]
├─ [email protected]
├─ [email protected]
├─ [email protected]
├─ [email protected]
├─ [email protected]
├─ [email protected]
├─ [email protected]
├─ [email protected]
├─ [email protected]
├─ [email protected]
└─ [email protected]
Done in 14.16s.
Run the tests:
$ yarn e2e -- --baseUrl https://angular.io
yarn e2e v0.27.5
$ webdriver-manager update
webdriver-manager: using local installed version 12.0.6
[17:59:46] I/update - chromedriver: file exists /Users/David/Projects/github/spektrakel-blog/angular-protractor-cucumber/node_modules/webdriver-manager/selenium/chromedriver_2.31.zip
[17:59:46] I/update - chromedriver: unzipping chromedriver_2.31.zip
[17:59:47] I/update - chromedriver: setting permissions to 0755 for /Users/David/Projects/github/spektrakel-blog/angular-protractor-cucumber/node_modules/webdriver-manager/selenium/chromedriver_2.31
[17:59:47] I/update - chromedriver: chromedriver_2.31 up to date
[17:59:47] I/update - selenium standalone: file exists /Users/David/Projects/github/spektrakel-blog/angular-protractor-cucumber/node_modules/webdriver-manager/selenium/selenium-server-standalone-3.4.0.jar
[17:59:47] I/update - selenium standalone: selenium-server-standalone-3.4.0.jar up to date
[17:59:48] I/update - geckodriver: file exists /Users/David/Projects/github/spektrakel-blog/angular-protractor-cucumber/node_modules/webdriver-manager/selenium/geckodriver-v0.18.0.tar.gz
[17:59:48] I/update - geckodriver: unzipping geckodriver-v0.18.0.tar.gz
[17:59:48] I/update - geckodriver: setting permissions to 0755 for /Users/David/Projects/github/spektrakel-blog/angular-protractor-cucumber/node_modules/webdriver-manager/selenium/geckodriver-v0.18.0
[17:59:48] I/update - geckodriver: geckodriver-v0.18.0 up to date
$ protractor "--baseUrl" "https://angular.io"
(node:57809) [DEP0022] DeprecationWarning: os.tmpDir() is deprecated. Use os.tmpdir() instead.
[17:59:49] I/launcher - Running 1 instances of WebDriver
[17:59:49] I/direct - Using ChromeDriver directly...
Feature: Search
As a developer using Angular
I need to look-up classes and guidelines
So that I can concentrate on building awesome applications
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)
3 steps (3 passed)
0m07.073s
[18:00:03] I/launcher - 0 instance(s) of WebDriver still running
[18:00:03] I/launcher - chrome #01 passed
Done in 19.48s.