Beyond Page Objects

Post on 30-Oct-2014

8.775 views 0 download

Tags:

description

 

Transcript of Beyond Page Objects

Beyond Page Objects

Dante BrionesSeleniumConf 5 April 2011

http://electronicingenuity.comdante@electronicingenuity.com

@dantebriones

Wednesday, 06 April, 2011

Let’s Start With A Story

Wednesday, 06 April, 2011

A long time ago* in an investment bank

far, far away...

*(2005)

Wednesday, 06 April, 2011

Our Hero

Wednesday, 06 April, 2011

A Chance Encounter

Wednesday, 06 April, 2011

A New Weapon

Wednesday, 06 April, 2011

Engaging The Enemy

Wednesday, 06 April, 2011

Victory

MUCH more readable!

Wednesday, 06 April, 2011

What’s The Moral?

(hint: it’s not about the developer, the author, or the book)

Wednesday, 06 April, 2011

The Details

Wednesday, 06 April, 2011

Why Page Objects?

Wednesday, 06 April, 2011

Why Page Objects?

Wednesday, 06 April, 2011

Why Page Objects?

Wednesday, 06 April, 2011

Tests Are Code

They need TLC

Wednesday, 06 April, 2011

Readability

selenium.click(“id=itemAction1138”);assertEquals(“1”, selenium.getText(“css=#cart .count”));

vs.item.clickAddToCart();assertEquals(1, cart.getItemCount());

Wednesday, 06 April, 2011

Stay DRY

Test class:

selenium.click(“id=zoom_out_action”);

selenium.click(“id=zoom_out_action”);

vs.Test class:

map.zoomOut();

map.zoomOut();

Map class:

selenium.click(“id=zoom_out_action”);

Wednesday, 06 April, 2011

Robustitude

Thread.sleep(250); //wait for button to appear

selenium.click(“id=a_button”);

Thread.sleep(500); //wait for results to show up

assertEquals(selenium.getText(“id=results”),

“False Negatives suck!”);

Wednesday, 06 April, 2011

Sensible Names

• catalogItem.addToCart()

• blogEntry.post()

• auction.bid(25.00)

Wednesday, 06 April, 2011

Small Classes

Wednesday, 06 April, 2011

How Do We Get There?

• Refactoring

• Apply Design Patterns

• Continuous Improvement

Wednesday, 06 April, 2011

How Do We Get There?

Wednesday, 06 April, 2011

Context

• Several consulting gigs over the past few years

• Current client:

• consumer-facing RIA

• tests written with Selenium 1 API in Java

Wednesday, 06 April, 2011

PRO TIP

Use WebDriver

Wednesday, 06 April, 2011

Page Objects Recap

• Model the application’s UI

• Expose methods that reflect the things a user can see and do on that page

• Hides the details of telling the browser how to do those things

Wednesday, 06 April, 2011

Page Object Benefits

• Readable tests

• Reduced or eliminated duplication

• Self-verification

• Reusability

Wednesday, 06 April, 2011

So Why Go “Beyond”?

Web Apps Are Different Now

Wednesday, 06 April, 2011

Why Go “Beyond”?

meh

OMG SOOO MUCH BETTER

Wednesday, 06 April, 2011

Why Go “Beyond”?

Wednesday, 06 April, 2011

Why Go “Beyond”?

Because modern web apps are

dynamic and rich

Wednesday, 06 April, 2011

Example: Pivotal Tracker

Wednesday, 06 April, 2011

Wednesday, 06 April, 2011

Modeling The UI

public class IssueSummary {

public IssueDetail expand() {...}

}

public class IssueDetail {

public void save() {...}

public void cancel() {...}

public String getStoryId() {...}

}

Wednesday, 06 April, 2011

What’s This, Then?

I call them “Page Components”

Wednesday, 06 April, 2011

Method Chaining: Focus Stays Put

public class IssueDetail {

public void save() {...}

public IssueDetail addComment(String comment) {

...

return this;

}

}

issueDetail.addComment().save();

Wednesday, 06 April, 2011

Method Chaining: Focus Moves Elsewhere

public class IssueDetail {

public ConfirmationDialog delete() {

...

return new ConfirmationDialog();

}

}

issueDetail.delete().confirm();

or

issueDetail.delete().cancel();

Wednesday, 06 April, 2011

Page Component Recap

• Just like Page Objects... but littler

• Use return types to model the user experience

Wednesday, 06 April, 2011

ComponentFactory

Do you like to type? I don’t.

Wednesday, 06 April, 2011

ComponentFactory

Job Satisfaction = 1/(keystrokes required write useful code)

Wednesday, 06 April, 2011

ComponentFactorypublic class ComponentFactory {

public IssueSummary issueSummary() {

return new IssueSummary(...);

}

}

public abstract BaseTestCase {

protected ComponentFactory withComponent() {

return new ComponentFactory();

}

}

Wednesday, 06 April, 2011

ComponentFactory

new IssueSummary(selenium).expand();

vs.

withComponent().issueSummary().expand();

Wednesday, 06 April, 2011

ComponentFactory Recap

• Interactive documentation

• Easier entry point into component creation

• Another way to use strong typing to make the tests easier to write

Wednesday, 06 April, 2011

Indexed Components

Wednesday, 06 April, 2011

Indexed Components

IssueSummary summary =

withComponent().backlog().issueSummary(0);

assertEquals(summary.getTitle(), “some title”);

Wednesday, 06 April, 2011

Indexed Components

CSS locators:

Backlog = #backlog

First issue summary = #backlog .issue-summary:nth(0)

“Expand” button = #backlog .issue-summary:nth(0) .expand

Title = #backlog .issue-summary:nth(0) .title

Wednesday, 06 April, 2011

The Locator Classpublic class Locator {

protected int index;

public Locator(String rootLocator, int index) {

this.rootLocator = rootLocator;

this.index = index;

}

public String of(String containedElement) {

return String.format(rootLocator, index) +

" " + containedElement;

}

}

Wednesday, 06 April, 2011

Using A Locatorpublic IssueDetail expand() {

selenium.click(“#backlog .issue-summary:nth(0) .expand”);

...

}

vs.

public IssueDetail expand() {

selenium.expand(locator.of(“.expand”));

...

}

Wednesday, 06 April, 2011

Synchronization Issues

Two kinds:

1. Element not present yet

2. Element content not updated yet

Wednesday, 06 April, 2011

Thread.sleep()

NO.*

STEP AWAY FROM THE KEYBOARD.

Wednesday, 06 April, 2011

Thread.sleep()

NO.*

STEP AWAY FROM THE KEYBOARD.

(*unless you have no other choice)

Wednesday, 06 April, 2011

Thread.sleep()

Just be sure to wrap it in an appropriately named method:

Wednesday, 06 April, 2011

Thread.sleep()

Just be sure to wrap it in an appropriately named method:

• sleepAsALastResortWhenNothingElseWorks()

Wednesday, 06 April, 2011

Thread.sleep()

Just be sure to wrap it in an appropriately named method:

• sleepAsALastResortWhenNothingElseWorks()

• sleepToMakeThisTestMoreFlakyThusEnsuringMyJobSecurity()

Wednesday, 06 April, 2011

Thread.sleep()

Just be sure to wrap it in an appropriately named method:

• sleepAsALastResortWhenNothingElseWorks()

• sleepToMakeThisTestMoreFlakyThusEnsuringMyJobSecurity()

• sleepBecauseImTooLazyToSolveThisProperly()

Wednesday, 06 April, 2011

Use The Wait Class

...to delay the test until the app exhibits certain behavior rather than waiting for an arbitrary amount of time.

Wednesday, 06 April, 2011

Two Options

1. Subclass DefaultSelenium and override the click() method

2. Extract a BaseComponent class that provides a click() method for use by all subclasses

Wednesday, 06 April, 2011

Next Issue

The element is present in the DOM, but doesn't contain the right data yet.

Wednesday, 06 April, 2011

Cleanest Solution

Build the app for testability

Wednesday, 06 April, 2011

Next Best Solution

public IssueDetail addComment() {

int initialCount = getCommentCount();

selenium.type(locator.of(“.comment-entry”));

selenium.click(locator.of(“.add-comment”));

waitUntilCommentCountIs(initialCount + 1);

return this;

}

Wednesday, 06 April, 2011

Final Thoughts

Beyond “Beyond”?

Wednesday, 06 April, 2011

Thank You!

http://electronicingenuity.comdante@electronicingenuity.com

@dantebriones

Wednesday, 06 April, 2011

References

• Dan North on Best Practices:

• http://www.viddler.com/explore/kvarnhammar/videos/6/

• Adam Goucher on built-in test synchronization:

• http://saucelabs.com/blog/index.php/2011/02/advanced-selenium-synchronization-with-latches/

Wednesday, 06 April, 2011