Protractor

Fully automated E2E testing

Created by Sam Vloeberghs / @samvloeberghs

whoami?

Sam Vloeberghs

Sam Vloeberghs
Freelance Frontend Architect

Questions?

mail / hangout (sam.vloeberghs)
follow / tweet @samvloeberghs
connect

What is protractor

  • Google product
  • successor of the scenario runner
  • E2E testing framework
  • runs on top of selenium webdriver

Unit vs E2E testing

  • test units / atomic parts of app
  • mocks out dependencies
  • code perspective

vs

  • test full functional parts of app
  • integrate different aspects
  • user behaviour perspective

Best practices (1)

Based on the styleguide by Carmen Popoviciu

  • do not cheat on your tests
  • don't retest what's unit tested
  • use proper naming for elements
  • wrap common elements in page objects
  • don't use the Xpath selector
  • don't mock unless you need to
  • refresh / navigate before every test

Locators & functions

  • find elements
  • perform actions
  • retrieve values

All operations are async!

Locators


element(by.id('addbutton'));	// 
element(by.css('some-css'));	// 
element(by.css('.special'));	// 
element(by.model('name'));	// 
element(by.binding('name'));	// 

element(by.css('my-css'));	// is the same as
$('my-css');			// short cut selector  ( css selectors )
						

Protractor documentation : locators

Actions


var el = element(locator);

// Click on the element
el.click();

// Send keys to the element (usually an input)
el.sendKeys('my text');

// Clear the text in an element (usually an input)
el.clear();

// Get the value of an attribute, for example, get the value of an input
el.getAttribute('value');
						

Protractor documentation : actions

Finding multiple elements


element.all(by.css('.selector')).then(function(elements) {
	// elements is an array of ElementFinders.
});

// Number of elements.
element.all(locator).count();

// Get by index (starting at 0).
element.all(locator).get(index);

// First and last.
element.all(locator).first();
element.all(locator).last();
						

Protractor documentation : multiple elements

Page objects

  • extra abstraction tests - code
  • can be reused
  • don't do assertions in page objects

avoid :(


/* question.spec.js */
describe('Question page', () => {
    it('should answer any question', () => {
        let question = element(by.model('question.text'));
        let answer = element(by.binding('answer'));
        let button = element(by.css('.question-button'));

        question.sendKeys('What is the purpose of life?');
        button.click();
        expect(answer.getText()).toEqual("Chocolate!");
    });
});
                        

encapsulate


/* question.page.js */
var QuestionPage = () => {
	this.question = element(by.model('question.text'));
	this.answer = element(by.binding('answer'));
	this.button = element(by.className('question-button'));

	this.ask = (question) => {
		this.question.sendKeys(question);
		this.button.click();
	};
};
module.exports = QuestionPage;
                        

better :)


/* question.spec.js */
let QuestionPage = require('./question.page');

describe('Question page', () => {
	let question = new QuestionPage();

	it('should ask any question', () => {
		question.ask('What is the purpose of meaning?');
		expect(question.answer.getText()).toEqual('Chocolate');
	});
});
                    

Project structure

  • locating test files should be instant
  • clearly seperate from unit tests
  • folder structure more readable

Avoid :(


|-- project-folder
	|-- app
		|-- home
			home.html
			home.module.js
			home.controller.js
		|-- contacts
			contacts.html
			contacts.module.js
			contacts.controller.js
	app.module.js
	app.controller.js
	app.css
	index.html
	|-- test
		|-- unit
		|-- e2e
			home.pageObject.js
			home.spec.js
			contacts.pageObject.js
			contacts.spec.js
						

Better :)


|-- project-folder
|-- app
	|-- home
		home.html
		home.module.js
		home.controller.js
	|-- contacts
		contacts.html
		contacts.module.js
		contacts.controller.js
	app.js
	app.module.js
	app.controller.js
	app.css
	index.html
|-- test
	|-- unit
	|-- e2e
		|-- home
			home.pageObject.js
			home.spec.js
		|-- contacts
			contacts.pageObject.js
			contacts.spec.js
						

Demo!