In this talk, I would like to speak about best practices for writing e2e tests with Protractor. The styleguide that I will introduce, is a joint initiative of mine and @andresdom from Google. Some of the subjects that will be covered include why e2e testing is important, what e2e tests should cover, naming conventions, selector strategies, page objects, helper objects and performance considerations. That and lots of smileys obviously, because we wanted to smiley all the things ...right? ¯\_(ツ)_/¯
5. WHAT?
E2E testing is a means of verifying that all units of
an application interact as expected with each other
and that the system as a whole works as intended
43. Control Flow
● Non blocking API
● WebdriverJS/Protractor APIs are purely
asynchronous
44. Control Flow
● Non blocking API
● WebdriverJS/Protractor APIs are purely
asynchronous
● Every function returns a promise
45. Control Flow
it('should greet’, function() {
browser.get('#/hello-world’);
var name = element(by.model(‘name’));
var greetBtn = element(by.tagName(‘button’));
var greeting = element(by.binding(‘greeting’));
name.sendKeys('DevfestRo');
greetBtn.click();
expect(greeting.getText()).toEqual('Hi DevfestRo!');
// ¯_(ツ)_/¯
});
46. Control Flow
● WebDriverJS maintains a queue of scheduled tasks
(pending promises), called the control flow
47. Control Flow
● WebDriverJS maintains a queue of scheduled tasks
(pending promises), called the control flow
● Each task is executed once the one before it in the
queue is finished
48. Control Flow
● WebDriverJS maintains a queue of scheduled tasks
(pending promises), called the control flow
● Each task is executed once the one before it in the
queue is finished
● Protractor adapts Jasmine so that each spec
automatically waits until the control flow is empty
before exiting
56. Use Page Objects to interact with the page under test
WHY?
Encapsulate information about the elements on the page under test
They can be reused across multiple tests
Decouple the test logic from implementation details
57. // avoid
/* question.spec.js */
describe('Question page', function() {
it('should answer any question', function() {
var question = element(by.model('question.text'));
var answer = element(by.binding('answer'));
var button = element(by.css('.question-button'));
question.sendKeys('What is the purpose of life?');
button.click();
expect(answer.getText()).toEqual("Chocolate!");
});
});
58. // recommended
/* question.spec.js */
var QuestionPage = require('./question.page');
describe('Question page', function() {
var question = new QuestionPage();
it('should ask any question', function() {
question.ask('What is the purpose of meaning?');
expect(question.answer.getText()).toEqual('Chocolate');
});
});
60. Declare functions for operations that require more than
one step
WHY?
Most elements are exposed by the Page Object and can be used directly in the test
Doing otherwise adds unnecessary complexity
61. Don't make any assertions in your Page Objects
WHY?
It is the responsibility of the test to do all the assertions
Reader of the test should be able to understand the behavior of the application by
looking at the test only
62. Prefer protractor locators when possible
WHY?
Access elements easier
Code is less likely to change than markup
More readable locators
64. Prefer by.id and by.css when no protractor locators are
available
WHY?
Access elements easier
Select markup that is less likely to change
More readable locators
65. NEVER use xpath
WHY?
Markup is very easily subject to change
xpath has performance issues
Unreadable locator