a journey beyond the page object pattern

31
SCREENPLAY A Journey Beyond The PageObject Pattern Antony Marcano Jan Molak Kostas Mamalis

Upload: riverglide

Post on 09-Jan-2017

8.702 views

Category:

Technology


2 download

TRANSCRIPT

Page 1: A journey beyond the page object pattern

SCREENPLAYA Journey Beyond The PageObject Pattern

Antony Marcano Jan Molak Kostas Mamalis

Page 2: A journey beyond the page object pattern

CONTACT• Kostas Mamalis

@agiletestinguk

[email protected]

• Antony Marcano@AntonyMarcano

[email protected]

• Jan Molak@JanMolak

[email protected]

Page 3: A journey beyond the page object pattern

CONTEXT

Page 4: A journey beyond the page object pattern

IN THE BEGINNING• Long established financial institution

• Used Scrum with 2 week Sprints

• Outsourced development to large consultancy

• No automated acceptance tests (few unit tests)

Page 5: A journey beyond the page object pattern

ALONG THE JOURNEY• Realised that automated tests were

essential

• Wrote lots of Cucumber tests

• Backed by Selenium/WebDriver

• Used the PageObject Pattern

Page 6: A journey beyond the page object pattern

SELENIUM AND

THE PAGE OBJECT PATTERN

Illustrated with the Pet Clinic

Page 7: A journey beyond the page object pattern
Page 8: A journey beyond the page object pattern

TESTING THE PET CLINIC

Page 9: A journey beyond the page object pattern

WEBDRIVER EXAMPLE

DesiredCapabilities capabilities = new DesiredCapabilities();WebDriver driver = new PhantomJSDriver(desiredCapabilities());

driver.get(baseUrl+"owners/find.html");

driver.findElement(By.cssSelector("#search-owner-form button")).click();

assertThat( driver.findElements(By.cssSelector("owners tbody tr")).size(), is(10));

Page 10: A journey beyond the page object pattern

FINDING ALL OWNERS - WEBDRIVER EXAMPLE

DesiredCapabilities capabilities = new DesiredCapabilities();WebDriver driver = new PhantomJSDriver(desiredCapabilities());

driver.get(baseUrl+"owners/find.html");

driver.findElement(By.cssSelector("#search-owner-form button")).click();

assertThat( driver.findElements(By.cssSelector("owners tbody tr")).size(), is(10));

Page 11: A journey beyond the page object pattern

FINDING ALL OWNERS - PAGEOBJECT EXAMPLE

DesiredCapabilities capabilities = new DesiredCapabilities();WebDriver driver = new PhantomJSDriver(desiredCapabilities());

driver.get(baseUrl+"owners/find.html");

FindOwnersPage findOwners = PageFactory.initElements(driver, FindOwnersPage.class);

OwnersPage owners = findOwners.findWith(EMPTY_SEARCH_TERMS);assertThat(owners.numberOfOwners(), is(10));

Page 12: A journey beyond the page object pattern

PROBLEMS AROSE

• Large PageObject classes

• Brittle test-code (less than raw Selenium)

• Duplication across PageObjects for each of the ‘portals’

Page 13: A journey beyond the page object pattern

THEY TRIED THE FOLLOWING

• Separate behaviour into Navigation Classes

• Reduce duplication with inheritance

Causing ...

• Large Navigation classes

• Deep inheritance hierarchy

Page 14: A journey beyond the page object pattern

EFFECTS ON THE TEAM• Took longer and longer to add new tests• Got harder to diagnose problems• Low trust in the ‘test framework’ and Cucumber• Reduced faith

in automated testing• Impacted morale

Page 15: A journey beyond the page object pattern

WHAT WAS THE ANSWER?

Page 16: A journey beyond the page object pattern

Antony Marcano at first AAFTT in 2007

Page 17: A journey beyond the page object pattern

THE INSIGHT

Roles ←Who➥ Goals ←Why

➥ Tasks ←What➥ Actions ←How

Inspired by Kevin Lawrence’s talk at the first AAFTT in 2007More of his thinking here: http://www.developertesting.com/archives/month200710/20071013-In%20Praise%20of%20Abstraction.html

Page 18: A journey beyond the page object pattern

2008 - JNARRATE@Test public void should_be_able_to_edit_a_page() { Given.thatThe(wiki).wasAbleTo(beAtThe(PointWhereItHasBeen.JUST_INSTALLED)); And.thatThe(user).wasAbleTo(navigateToTheHomePage()); And.thatThe(user).wasAbleTo(navigateToTheHomePage());

When.the(user).attemptsTo( changeTheContent().to("Welcome to Acceptance Test Driven Development") );

Then.the(textOnTheScreen().ofThe(user)). shouldBe("Welcome to Acceptance Test Driven Development");}Playing with fluent APIs and started to explore the model of Tasks & Actions(although back then the labels I used more like Kevin’s labels).

task

task

task

Page 19: A journey beyond the page object pattern

2009 - SCREENPLAY - A TASK

public void perform() {

you.need(To.doTheFollowing( // actionsClick.onThe(OptionsMenu.EDIT_BUTTON),ClearTheContent.ofThe(Editor.CONTENT_PANEL),

Type.theText(newContent).intoThe(Editor.CONTENT_PANEL),

Click.onThe(Editor.SAVE_BUTTON)));

}

Page 20: A journey beyond the page object pattern

Actor

2012 - THE JOURNEY PATTERN

Tasks

Abilities Actions Screen

Elements

contains

enable

performs

composed ofhas

interact with

Page 21: A journey beyond the page object pattern

JOURNEY PATTERN APPLIED JUNIT

Roles ← Who

➥ Goals ← Why

➥ Tasks ← What

➥ Actions ← How

Actor theReceptionist = new Actor().with(WebBrowsing.ability());

@Test public void should_find_all_owners_by_default

theReceptionist.attemptsTo(Go.to(findOwnersScreen.url()),Search.forOwnersWith(EMPTY_SEARCH_TERMS),Count.theNumberOfOwners()

);

Enter.the(searchTerms). into(findOwnersScreen.searchTerms),Click.onThe(findOwnersScreen.searchButton)

Page 22: A journey beyond the page object pattern

JOURNEY PATTERN APPLIED CUCUMBER

Roles ← Who

➥ Goals ← Why

➥ Tasks ← What

➥ Actions ← How

As a Pet Clinic Receptionist

Scenario: Find all owners by default

When I search for owners with BLANK search terms@When(“^I search for owners with BLANK search terms$”)

theReceptionist.attemptsTo(Search.forOwnersWith(EMPTY_SEARCH_TERMS)

);

Enter.the(searchTerms). into(findOwnersScreen.searchTerms),Click.onThe(findOwnersScreen.searchButton)

Page 23: A journey beyond the page object pattern

PUTTING IT ALL TOGETHERActor theReceptionist = new Actor().with(WebBrowsing.ability());

theReceptionist.attemptsTo( Go.to(findOwnersScreen.url()), Search.forOwnersWith(EMPTY_SEARCH_TERMS), Count.theNumberOfOwners());

assertThat(theReceptionist.sawThatThe(numberOfOwners()),was(theExpectedNumberOfOwners)

);

Page 24: A journey beyond the page object pattern

A TASK…

private static String searchTerms;

@Overridepublic void performAs(Actor asAReceptionist) { asAReceptionist.attemptTo( Enter.the(searchTerms).into(findOwnersScreen.searchTerms), Click.onThe(findOwnersScreen.searchButton) );}

public SearchForOwnersWith(String searchTerms) { this.searchTerms = searchTerms;}

Page 25: A journey beyond the page object pattern

A SCREEN

@Url("owners/find.html")public class FindOwnersScreen extends WebScreen {

@LocateBy(css="#search-owner-form input") public ScreenElement searchTerms;

@LocateBy(css="#search-owner-form button") public ScreenElement searchButton;}

Page 26: A journey beyond the page object pattern

AN ACTIONpublic class Enter extends WebDriverInteraction implements Perform { private String text; private ScreenElement field;

public void performAs(Actor actor) { web(actor).findElement(field.locator()).sendKeys(text); }

public Enter(String text) { this.text = text; }

public static Enter the(String text) {return new Enter(text);}

public Perform into(ScreenElement field) { this.field = field; return this; }}

Page 27: A journey beyond the page object pattern

PROBLEMS SOLVED• Smaller “Screen” classes

• Small, focused “Task” classes

• Readable code

• Consistent metaphor

• Minimal inheritance

• Removed need for duplication across behaviours previously in PageObjects or “Navigation” classes

Page 28: A journey beyond the page object pattern

DESIGN PRINCIPLES• DRY - navigational steps in one place

• Separation of Concerns - Page Structure and Actions separate

• Small Classes - easy to comprehend

• Single Responsibility - classes focused on one thing and one thing only

• Minimise conditional logic - navigational if-thens replaced with composable sequences

Page 29: A journey beyond the page object pattern

SCREENPLAY REVIVEDUnder development for everyone to use

Watch This Space!

Page 30: A journey beyond the page object pattern

CONTACT• Kostas Mamalis

@agiletestinguk

[email protected]

• Antony Marcano@AntonyMarcano

[email protected]

• Jan Molak@JanMolak

[email protected]

Page 31: A journey beyond the page object pattern

THANK YOU!