End-to-end testing with protractor for nonangular applications using modern ES6 javascript syntax
I will not repeat how testing is important and how you should test your application to add confidence and to verify correctness. If you are reading this you want to start E2E testing your application and searching what tools to use.
E2E Testing definition from angular docs
As applications grow in size and complexity, it becomes unrealistic to rely on manual testing to verify the correctness of new features, catch bugs and notice regressions. Unit tests are the first line of defense for catching bugs, but sometimes issues come up with integration between components which can’t be captured in a unit test. End-to-end tests are made to find these problems.
Protractor test runner
Protractor is an end-to-end test framework for AngularJS applications. Protractor runs tests against your application running in a real browser, interacting with it as a user would.
Protractor can be used for non angular applications
browser.ignoreSynchronization = true; will stop waiting for angular and will allow us to use protractor for testing server rendered or single page web applications not written using angular framework.
Example of running protractor test
Test runner will start one or more browser instances and output results in terminal.
Motivation to create protractor-starter
The greatest barrier for starting with end-to-end testing is tooling setup.
I want to give you a good starting point for your own E2E testing.
protractor-starter: https://github.com/marcelmokos/protractor-starter
Goals
- Modern javascript ES6 syntax
- No configuration required
- One command for setup and run
- Linting and automatic code style fixing using eslint build in, specially for protractor and jasmine
- Test examples using page object pattern
Modern ES6 Javascript
I can not see ES5 syntax anymore, this example is written exclusively in ES6.
There is no visible compilation step, you have correct line numbers in error stack traces and logs which are important for developer experience.
E2E tests a can use a feature of in-memory babel compilation. File .babelrc is setting presets for compilation.
The protractor test runner than can be called with prepended babel-node command and we do not have to think about compilation in the protractor config and tests.
“protractor”: “babel-node node_modules/.bin/protractor protractor-conf.js”,
Note that babel-node is not suitable for running node server in production because compiled source code is held in memory but is ideal for compiling E2E tests.
Async Await Promises in tests
Alternative syntax to Promise.then() allows awaiting for promise resolve without chaining.
WebDriverJS (and thus, Protractor) APIs are entirely asynchronous. All functions return promises. WebDriver Control Flow will try to resolve all promises in the queue before continuing. Async functions in body of expect(), beforeAll(), beforeEach(), afterAll(), afterEach() will resolve promises before continuing to next code block.
Example function
Function element.getAttribute("id") returns Promise resolving to <String>. Function getInputsLabelElement returns Promise resolving to <ElementFinder>.
Function is declared as async and is awaiting inputId value.
Example test
When async await is used then there is more control over test run and Promise chaining and the callback function is avoided.
Why not use typescript
I was writing protractor tests using typescript and I feel that it is unnecessary to complicate tests with types and interfaces. Intellisense in IDE will provide autocompletion and eslint will take care of most common mistakes and styling problems for protractor with recommended settings.
Provided protractor configuration
The configuration is simple run every test file in "spec" folder with "*.spec.js" extension.
specs: [“spec/**/*.spec.js”],
Why jasmine-spec-reporter
Default reporter was not working well when more browser instances were used. Default jasmine dot reporter was providing only summary on the end of the tests and jasmine-spec-reporter is providing detailed output during test run.
Get protractor starter
fork protractor-starter or clone repository directly
git clone https://github.com/marcelmokos/protractor-starter.git
Run tests
On single browser instance
yarn test
On multiple browser instances in parallel
yarn run test:multiple // this run will ignore fdescribe and run all test files
One command for setup and run. Both commands will install and run linting before starting protractor.
Why "yarn" instead of "npm"
This decision was done because "yarn" is faster.
To make it simple on every run all dependencies are installed. All dependencies are in project and nothing other then node and yarn needs to be installed globally.
Strong linting and automatic code style fixing
Set up linting is not easy and set up linting to work with testing frameworks can be even more complicated.
This project has linting setup in .eslintrc.yml config and inherit settings from eslint-config-airbnb-base and eslint-plugin-protractor.
This project has linting setup in .eslintrc.yml setting is opinionated but defaults came from eslint-airbnb-config. Incompatible style is marked as error to not allowing to commit incompatible style.
Automatic fixes for fixable problems like missing semicolons is performed by prettier and eslint -fix. Which helps maintain code style.
Goal is to improve code readability, style and ease code reviews.
There are developers that do not use proper code editor or IDE which will provide linting and suggestions eslint is command tool and will run everywhere.
VCS pre-commit hook linting
Pre-commit hooks are scripts that run before each commit.
Decision of strong linting requires setting up tool to not allow inconsistent code to be committed into a repository.
"pre-commit": [
"lint:pre-commit"
],
Eslint rules that are enforced only in pre-commit hook are
- "no-console", "no-debuger" allows using console for debugging but will not allow to commit debugging statements
- "protractor/no-browser-pause" allows to use browser.pause() for debugging but will not allow to commit this statement
Page object pattern and feature testing
Another barrier is that there are no good examples of tests with page Object pattern.
This project includes examples of testing single page app using Page Object pattern and feature testing.
spec
├── creatix
│ ├── Common.spec.js
│ ├── Homepage.spec.js
│ ├── Navigation.spec.js
│ ├── ContactForm.spec.js
│ └── pages // page objects
│ ├── About.js
│ ├── Blog.js
│ ├── Careers.js
│ ├── Common.js
│ ├── Contact.js
│ ├── ContactForm.js
│ ├── Homepage.js
│ └── Work.js
└── utils
├── Page.js
└── UIComponent.js
Page object pattern
All selectors and functions should be placed in Page Object Class. Class should holds selectors and actions for given page or component.
Page object files should export only one Page Class as default export and extends spec/utils/Page.js whitch extends spec/utils/UIElement.js and implement abstract property ‘selector’.
This will allow you to use isDisplayed and waitUntilDisplayed functions and you can avoid repeating boilerplate for this functionality which is needed because there is no synchronisation as with angular application.
Feature testing
The files structure of the project should wrap around features instead of pages.
Dependencies
- node https://nodejs.org/en/download/
- yarn https://yarnpkg.com/en/docs/install
- chrome https://www.google.com/chrome/browser/desktop/index.html
Documentation
protractor-starter
Protractor
- web: http://www.protractortest.org/
- Page Object Pattern: http://www.protractortest.org/#/page-objects
Jasmine
Eslint
- web: http://eslint.org/
- eslint-config-airbnb-base:
github: https://github.com/airbnb/javascript
npm: https://www.npmjs.com/package/eslint-config-airbnb-base - eslint-plugin-protractor:
github: https://github.com/alecxe/eslint-plugin-protractor
npm: https://www.npmjs.com/package/eslint-plugin-protractor