Nils Winkler
FrankfurtJS, October 30th 2014
This presentation gives a high level overview of Protractor and why/how to use it.
It does not go into detail on the Protractor API/syntax.
For more information on the Protractor API, please refer to the official documentation.
Different kinds of tests for different purposes:
Use the grunt-protractor-runner plugin:
npm install grunt-protractor-runner --save-dev
Automatic download of the WebDriver binaries as part of npm install (in package.json):
"scripts": { "install": "node node_modules/grunt-protractor-runner/node_modules/\ protractor/bin/webdriver-manager update" }
protractor.conf.js:
exports.config = { specs: ['test/**/*.spec'], baseUrl:'http://localhost:9000/', };
Point it to your test files.
Define the URL to test.
Gruntfile.js:
protractor: { options: { keepAlive: true, configFile: "protractor.conf.js" }, run: {} }
Use standard Jasmine syntax
describe('my app', function() { beforeEach(function() { }); it('should do something', function() { expect(...); }); });
Deploy/Start your web application
Run the tests
grunt protractor:run
Watch in awe!
Select DOM elements by ID, CSS, or by Angular model:
var fooForm = element(by.id('foo')); var barElements = foorForm.all(by.css('.bar')); var input = fooForm.element(by.model('user.name'));
Each test step is executed asynchronously, waiting for the Angular digest cycle to finish.
You can also use promises:
fooForm.evaluate('user.name').then(function (name) { expect(name).toBe('Foo'); });
The evaluate function allows you to take a look at the element's Angular scope.
Protractor is primarily used to test Angular apps, since it integrates really well with the Angular lifecycle and scope.
Since it is just a wrapper around Selenium WebDriver, it can also be used to test non-Angular apps. Simply apply this change before running your test:
browser.ignoreSynchronization = true;
Protractor will now no longer wait for Angular to load, and you can use it to test any web application.
Use unique ID values on your controls to allow easy identification of the screen elements.
Make sure the ID values are consistent and don't change across test invocations.
Try to auto-generate the ID values or work with conventions.
Tests can be run in parallel against multiple browsers:
multiCapabilities: [{ 'browserName': 'firefox' }, { 'browserName': 'chrome' }]
Browser configuration documentation: https://github.com/angular/protractor/blob/master/docs/browser-setup.md
Keyboard Input: Some operating systems (e.g. Mac OS X) handle keyboard input in a non-standard way.
Animations: Take rendering speed of the OS/browser into account.
var webDriver = protractor.getInstance().driver; webDriver.sleep(500);
or - better:
browser.wait(element.isDisplayed);
Since Protractor is written as a Node package, you can (and should!) create your own reusable test libraries.
var fooApp = require('foo-app-test'); fooApp.session.login('john.doe', '12345');
Create reusable libraries for
Just like with regular unit tests, get a feeling for how much of your code is covered by your tests.
Remember: 100% code coverage does not mean that your code is error-free!
To create xUnit-compatible output, which is understood by most Continuous Integration systems, install the jasmine-reporters package:
npm install --save-dev jasmine-reporters
protractor.conf.js:
onPrepare: function() { require('jasmine-reporters'); jasmine.getEnv().addReporter( new jasmine.JUnitXmlReporter('xunit-reports/', true, true)); }
Then point your Continuous Integration tool to the xunit-reports directory for reading the test results.
Trade-off:
npm install --save-dev grunt-env npm install --save-dev grunt-shell-spawn
Gruntfile.js:
grunt.initConfig({ // ... shell: { xvfb: { command: 'Xvfb :99 -ac -screen 0 1600x1200x24', options: { async: true } } }, env: { xvfb: { DISPLAY: ':99' } } // .... }); grunt.registerTask('protractor-xvfb', [ 'shell:xvfb', 'env:xvfb', 'protractor:run', 'shell:xvfb:kill' ]);