Beyond Page Objects

61
Beyond Page Objects Dante Briones SeleniumConf 5 April 2011 http://electronicingenuity.com [email protected] @dantebriones Wednesday, 06 April, 2011

description

 

Transcript of Beyond Page Objects

Page 1: Beyond Page Objects

Beyond Page Objects

Dante BrionesSeleniumConf 5 April 2011

http://[email protected]

@dantebriones

Wednesday, 06 April, 2011

Page 2: Beyond Page Objects

Let’s Start With A Story

Wednesday, 06 April, 2011

Page 3: Beyond Page Objects

A long time ago* in an investment bank

far, far away...

*(2005)

Wednesday, 06 April, 2011

Page 4: Beyond Page Objects

Our Hero

Wednesday, 06 April, 2011

Page 5: Beyond Page Objects

A Chance Encounter

Wednesday, 06 April, 2011

Page 6: Beyond Page Objects

A New Weapon

Wednesday, 06 April, 2011

Page 7: Beyond Page Objects

Engaging The Enemy

Wednesday, 06 April, 2011

Page 8: Beyond Page Objects

Victory

MUCH more readable!

Wednesday, 06 April, 2011

Page 9: Beyond Page Objects

What’s The Moral?

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

Wednesday, 06 April, 2011

Page 10: Beyond Page Objects

The Details

Wednesday, 06 April, 2011

Page 11: Beyond Page Objects

Why Page Objects?

Wednesday, 06 April, 2011

Page 12: Beyond Page Objects

Why Page Objects?

Wednesday, 06 April, 2011

Page 13: Beyond Page Objects

Why Page Objects?

Wednesday, 06 April, 2011

Page 14: Beyond Page Objects

Tests Are Code

They need TLC

Wednesday, 06 April, 2011

Page 15: Beyond Page Objects

Readability

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

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

Wednesday, 06 April, 2011

Page 16: Beyond Page Objects

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

Page 17: Beyond Page Objects

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

Page 18: Beyond Page Objects

Sensible Names

• catalogItem.addToCart()

• blogEntry.post()

• auction.bid(25.00)

Wednesday, 06 April, 2011

Page 19: Beyond Page Objects

Small Classes

Wednesday, 06 April, 2011

Page 20: Beyond Page Objects

How Do We Get There?

• Refactoring

• Apply Design Patterns

• Continuous Improvement

Wednesday, 06 April, 2011

Page 21: Beyond Page Objects

How Do We Get There?

Wednesday, 06 April, 2011

Page 22: Beyond Page Objects

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

Page 23: Beyond Page Objects

PRO TIP

Use WebDriver

Wednesday, 06 April, 2011

Page 24: Beyond Page Objects

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 25: Beyond Page Objects

Page Object Benefits

• Readable tests

• Reduced or eliminated duplication

• Self-verification

• Reusability

Wednesday, 06 April, 2011

Page 26: Beyond Page Objects

So Why Go “Beyond”?

Web Apps Are Different Now

Wednesday, 06 April, 2011

Page 27: Beyond Page Objects

Why Go “Beyond”?

meh

OMG SOOO MUCH BETTER

Wednesday, 06 April, 2011

Page 28: Beyond Page Objects

Why Go “Beyond”?

Wednesday, 06 April, 2011

Page 29: Beyond Page Objects

Why Go “Beyond”?

Because modern web apps are

dynamic and rich

Wednesday, 06 April, 2011

Page 30: Beyond Page Objects

Example: Pivotal Tracker

Wednesday, 06 April, 2011

Page 31: Beyond Page Objects

Wednesday, 06 April, 2011

Page 32: Beyond Page Objects

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

Page 33: Beyond Page Objects

What’s This, Then?

I call them “Page Components”

Wednesday, 06 April, 2011

Page 34: Beyond Page Objects

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

Page 35: Beyond Page Objects

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 36: Beyond Page Objects

Page Component Recap

• Just like Page Objects... but littler

• Use return types to model the user experience

Wednesday, 06 April, 2011

Page 37: Beyond Page Objects

ComponentFactory

Do you like to type? I don’t.

Wednesday, 06 April, 2011

Page 38: Beyond Page Objects

ComponentFactory

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

Wednesday, 06 April, 2011

Page 39: Beyond Page Objects

ComponentFactorypublic class ComponentFactory {

public IssueSummary issueSummary() {

return new IssueSummary(...);

}

}

public abstract BaseTestCase {

protected ComponentFactory withComponent() {

return new ComponentFactory();

}

}

Wednesday, 06 April, 2011

Page 40: Beyond Page Objects

ComponentFactory

new IssueSummary(selenium).expand();

vs.

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

Wednesday, 06 April, 2011

Page 41: Beyond Page Objects

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

Page 42: Beyond Page Objects

Indexed Components

Wednesday, 06 April, 2011

Page 43: Beyond Page Objects

Indexed Components

IssueSummary summary =

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

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

Wednesday, 06 April, 2011

Page 44: Beyond Page Objects

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

Page 45: Beyond Page Objects

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

Page 46: Beyond Page Objects

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

Page 47: Beyond Page Objects

Synchronization Issues

Two kinds:

1. Element not present yet

2. Element content not updated yet

Wednesday, 06 April, 2011

Page 48: Beyond Page Objects

Thread.sleep()

NO.*

STEP AWAY FROM THE KEYBOARD.

Wednesday, 06 April, 2011

Page 49: Beyond Page Objects

Thread.sleep()

NO.*

STEP AWAY FROM THE KEYBOARD.

(*unless you have no other choice)

Wednesday, 06 April, 2011

Page 50: Beyond Page Objects

Thread.sleep()

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

Wednesday, 06 April, 2011

Page 51: Beyond Page Objects

Thread.sleep()

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

• sleepAsALastResortWhenNothingElseWorks()

Wednesday, 06 April, 2011

Page 52: Beyond Page Objects

Thread.sleep()

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

• sleepAsALastResortWhenNothingElseWorks()

• sleepToMakeThisTestMoreFlakyThusEnsuringMyJobSecurity()

Wednesday, 06 April, 2011

Page 53: Beyond Page Objects

Thread.sleep()

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

• sleepAsALastResortWhenNothingElseWorks()

• sleepToMakeThisTestMoreFlakyThusEnsuringMyJobSecurity()

• sleepBecauseImTooLazyToSolveThisProperly()

Wednesday, 06 April, 2011

Page 54: Beyond Page Objects

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

Page 55: Beyond Page Objects

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

Page 56: Beyond Page Objects

Next Issue

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

Wednesday, 06 April, 2011

Page 57: Beyond Page Objects

Cleanest Solution

Build the app for testability

Wednesday, 06 April, 2011

Page 58: Beyond Page Objects

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

Page 59: Beyond Page Objects

Final Thoughts

Beyond “Beyond”?

Wednesday, 06 April, 2011

Page 61: Beyond Page Objects

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