Beyond Page Objects
-
Upload
dante-briones -
Category
Technology
-
view
8.773 -
download
0
description
Transcript of Beyond Page Objects
Beyond Page Objects
Dante BrionesSeleniumConf 5 April 2011
http://[email protected]
@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
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