Protractor
Luis Hernandez
Developer
lhernandez@nearsoft.com
What's Protractor?
Protractor is an open source E2E testing automation framework, designed specifically for AngularJS web applications. The Protractor automation tool is a Node.js program built on top of WebDriverJS.
Workflow
Protractor works as a Solution integrator, combining powerful technologies like Node.js, Jasmine, Selenium, Mocha, Cucumber, and Web driver.
Protractor is built on top of WebDriverJS
Testing system (NodeJS, Java, etc)
|
Webdriver (a.k.a. Selenium)
|
Your AngularJS App
Why is testing so important?
" Testing is about gaining confidence that your code does what you think it should do"
Whats the idea behind E2E testing?
- How would the users see my application?
- Is my backend communicating with my frontend?
- Can I release this code?
- It does NOT replace Unit Testing!
The dark side of E2E testing
- It needs a specific running environment
- It's hard to write
- It's difficult to debug
- It's hard to keep the tests up-to-date
Install
- Download Node.JS
-
$ sudo npm install protractor -g
-
$ sudo webdriver-manager update
Setup a conf.js file
exports.config = {
seleniumAddress: 'http://localhost:4444/wd/hub',
capabilities: {
'browserName': 'chrome'
},
specs: ['example-spec.js'],
jasmineNodeOpts: {
showColors: true
}
};
Write your tests using Jasmine and WebdriverJS
describe('by model', function() {
it('should find an element by text input model', function() {
var username = element(by.model('username'));
var name = element(by.binding('username'));
username.clear();
expect(name.getText()).toEqual('');
username.sendKeys('Jane Doe');
expect(name.getText()).toEqual('Jane Doe');
});
});
Protractor global variables
-
browser: browser.get()
-
element and by: element(by.model('yourName'))
-
protractor: protractor.Key
Basic example
// example-spec.js
describe('angularjs homepage', function() {
it('should greet the named user', function() {
browser.get('http://www.angularjs.org');
element(by.model('yourName')).sendKeys('Julie');
var greeting = element(by.binding('yourName'));
expect(greeting.getText()).toEqual('Hello Julie!');
});
});
Let's run it
First things first, open the terminal and start the webdriver server:
webdriver-manager start
After that, you can run Protractor in another terminal by typing:
protractor test/e2e/config.js // this is the relative path to your config.js file
Searching for elements on the page
element() vs element.all()
Single element
element( by.binding('appName') );
Collection of elements
element.all( by.css('[ng-click="openPage()"]') ).get(2).click();
by.binding
In your test
element( by.binding('myModel') );
In your application
<span ng-bind="myModel"></span>
<!-- or -->
<span>{{myModel}}</span>
by.css
In your test
element( by.css('[ng-click="sendMail()"]') );
In your application
<button ng-click="sendMail()">Send mail!</button>
Find out more in Protractor API
Executing Events
.click()
In your test
In your application
element( by.css('[ng-click="submit()"]') ).click();
<button ng-click="submit()"><button>
On Enter Press
In your test
In your application
element( by.model('commentText') ).sendKeys("Hi!", protractor.Key.ENTER);
<textarea ng-model="commentText"><textarea>
Promises and the Control Flow
Promises based
// Example of getText() promise
element( by.model('zipcode') ).getText()
.then(function(val) {
var num = val.substring(0, 4);
var isNum = !isNaN(num);
expect( isNum ).toBeTruthy();
});
Control Flow
WebDriverJS maintains a queue of pending promises, called the control flow, to keep execution organized.
it('should find an element by text input model', function() {
browser.get('#/home'); // (1) method browser.get
// (2) method by.binding
var login = element(by.binding('login'));
// (3) method getText
expect(login.getText()).toEqual('User');
});
Maintanable Tests
The big picture
- Page Objects - These are the js files where you map the elements and write the functions to perform actions;
- Exports and Require - This is how you connect your Page Objects to your Test Specs;
- Test specs - These are the js files where you write your tests using jasmine syntax.
Tests directory structure
projectfolder/
|-- css/
|-- js/
|-- img/
|-- tests/
|-- unit/
|-- e2e/
| |-- homepage/
| | |-- homepage.po.js
| | |-- *.spec.js
| |-- profile/
| | |-- profile.po.js
| | |-- *.spec.js
| |-- config.js
Page Objects
var AngularHomepage = function() {
this.nameInput = element(by.model('yourName'));
this.greeting = element(by.binding('yourName'));
this.get = function() {
browser.get('http://www.angularjs.org');
};
this.setName = function(name) {
this.nameInput.sendKeys(name);
};
};
Node.JS exports and require
var AngularHomepage = function() {
this.nameInput = element(by.model('yourName'));
this.greeting = element(by.binding('yourName'));
// ...
};
module.exports = AngularHomepage;
var AngularHomepage = require('./homepage.po.js');
describe('HomePage Tests', function() {
var angularHomepage = new AngularHomepage();
angularHomepage.nameInput.sendKeys('Rafael');
//...
});
Your Page Object file
Your Test file
Separate your tests in various test suites
exports.config = {
seleniumAddress: 'http://localhost:4444/wd/hub',
capabilities: { 'browserName': 'chrome' },
suites: {
homepage: 'tests/e2e/homepage/**/*Spec.js',
search: ['tests/e2e/contact_search/**/*Spec.js']
},
jasmineNodeOpts: { showColors: true }
};
protractor protractor.conf.js --suite homepage
Your Page Object file
Running specific suite of tests
Using Protractor in a non-AngularJS app
Using browser.driver
You only need to access the webdriver instance by using browser.driver:
browser.driver.findElement(by.css('[data-ptor="submit-btn"]'));
It can be even more elegant
conf.js
onPrepare: function() {
global.dvr = browser.driver;
}
test
dvr.findElement(by.css('[data-ptor="submit-btn"]'));
Protractor waits for Angular to finish its work
tests
beforeEach(function() {
return browser.ignoreSynchronization = true;
});
Protractor waits for Angular to finish its work
conf.js
onPrepare: function() {
global.isAngularSite = function(flag) {
browser.ignoreSynchronization = !flag;
};
}
tests
beforeEach(function() {
isAngularSite(false); // isAngularSite(true), if it's an Angular app!
});
Protractor waits for Angular to finish its work
conf.js
onPrepare: function() {
global.isAngularSite = function(flag) {
browser.ignoreSynchronization = !flag;
};
}
tests
beforeEach(function() {
isAngularSite(false); // isAngularSite(true), if it's an Angular app!
});
Final thoughts
- E2E testing is a complement to Unit testing
- Write your tests with scalability in mind
Reference
http://ramonvictor.github.io/protractor/slides/
Thanks.
Protractor
By Luis Hernandez
Protractor
- 772