An overview of different approaches to Page Objects and Domain Objects for GUI Automation. Examples use WebDriver and Java.
Full source code is available to download, For more details see the associated blog post.
SpotFlow: Tracking Method Calls and States at Runtime
Automation Abstraction Layers: Page Objects and Beyond
1. 1
Automation Abstractions:
Page Objects and Beyond
Alan Richardson
@eviltester
https://xp-dev.com/svn/AutomationAbstractions
www.SeleniumSimplified.com
www.EvilTester.com
www.CompendiumDev.co.uk
www.JavaForTesters.com
2. 2
What is Abstraction?
● Modelling
● Separation of concerns
● Logical vs Physical
● Functional vs Structural
● Interfaces vs Implementations
● Data / Entities / Persistence
● Functionality / Task Flow
● Goals / Strategies
● Layers – GUI, DB, HTTP
● Etc.
3. “I must create a system. or be
enslav'd by another Mans; I
will not reason & compare:
my business is to create”
William Blake, 1820
Jerusalem: The Emanation of the Giant Albion
3
http://www.blakearchive.org/exist/blake/archive/object.xq?objectid=jerusalem.e.illbk.10&java=no
4. https://xp-dev.com/svn/AutomationAbstractions
4
Example Test Without
Abstraction
@Before
public void startDriver(){
@Test
public void canCreateAToDoWithNoAbstraction(){
driver.get("http://todomvc.com/architecture-examples/backbone/");
int originalNumberOfTodos = driver.findElements(
By.cssSelector("ul#todo-list li")).size();
WebElement createTodo = driver.findElement(By.id("new-todo"));
createTodo.click();
createTodo.sendKeys("new task");
createTodo.sendKeys(Keys.ENTER);
assertThat(driver.findElement(
By.id("filters")).isDisplayed(), is(true));
int newToDos = driver.findElements(
By.cssSelector("ul#todo-list li")).size();
assertThat(newToDos, greaterThan(originalNumberOfTodos));
}
driver = new FirefoxDriver();
}
@After
public void stopDriver(){
driver.close();
driver.quit();
}
NoAbstractionTest.java
5. But this does use some
abstraction layers
LLooccaattoorr A Abbssttrraaccttioionns
WebElement Generic Element Abstraction
5
Example Test Without
Abstraction
@Before
public void startDriver(){
WebDriver Generic Browser Abstraction
@Test
public void canCreateAToDoWithNoAbstraction(){
Firefox Browser Abstraction
driver.get("http://todomvc.com/architecture-examples/backbone/");
int originalNumberOfTodos = driver.findElements(
By.cssSelector("ul#todo-list li")).size();
WebElement createTodo = driver.findElement(By.id("new-todo"));
createTodo.click();
createTodo.sendKeys("new task");
createTodo.sendKeys(Keys.ENTER);
assertThat(driver.findElement(
By.id("filters")).isDisplayed(), is(true));
int newToDos = driver.findElements(
Manipulation Abstractions
By.cssSelector("ul#todo-list li")).size();
assertThat(newToDos, greaterThan(originalNumberOfTodos));
}
driver = new FirefoxDriver();
}
@After
public void stopDriver(){
driver.close();
driver.quit();
}
NoAbstractionTest.java
6. 6
WebDriver provides abstractions
● Browser
● DOM
● Web Element
Tool vendors gain value from generic abstractions.
You gain value from 'domain' abstractions.
7. 7
Example Test With Abstraction
@Before
public void startDriver(){
@Test
public void canCreateAToDoWithAbstraction(){
TodoMVCUser user =
new TodoMVCUser(driver, new TodoMVCSite());
user.opensApplication().and().createNewToDo("new task");
ApplicationPageFunctional page =
new ApplicationPageFunctional(driver,
new TodoMVCSite());
assertThat(page.getCountOfTodoDoItems(), is(1));
assertThat(page.isFooterVisible(), is(true));
}
driver = new FirefoxDriver();
}
@After
public void stopDriver(){
driver.close();
driver.quit();
}
NoAbstractionTest.java
8. 8
Why Abstraction?
● Change implementations
● Single Responsibility – only changes when
necessary
● Makes automation readable and maintainable
● We can unit test some of our test code
● etc.
https://xp-dev.com/svn/AutomationAbstractions
9. 9
Some Abstraction Layer Categories
1) Data
– Generic Data Abstractions e.g. email, postcode
2) Physical
– Physical layout of your application e.g. pages,
components
– Navigation across pages
3) Domain
– Your application Entities domain e.g. user, account
4) Logical
– User actions, workflows
10. 10
Common Automation Abstractions
● Page Objects: pages, components, widgets
● Dom Element Abstractions: select, textbox, etc.
● Domain Objects: user, account, order
● Gherkin (Given/When/And/Then)
● Domain Specific Languages: code, keywords
● ...
https://xp-dev.com/svn/AutomationAbstractions
11. 11
Abstraction != Implementation
● Abstraction != Tool / Framework /
Implementation
● Gherkin != Cucumber
● Page Object != Page Factory / Slow Loadable
● DSL != Keyword Driven
If we want to get good at abstraction then we
need to model, split apart, make the relationships
clear, and be aware of our options.
12. 12
Page Objects
● The most obvious
automation abstraction
● What is it?
– A page? A Component?
● Do web applications still
have pages?
13. A Page Object that abstracts a Page
13
● Often has methods for
– Opening the page
– Accessing elements
– Doing stuff, and navigating as a side-effect
– Logical high level functionality e.g. login
– Low level physical functionality e.g.
typeUsername, clickLoginButton
Should a Page Object be responsible for all of
this?
14. 14
Page Object Design Decisions
● What methods does it have?
– Functional
● login(username, password),
● loginAs(user)
– Structural
● enterName(username), enterPassword(password),
clickLogin(), submitLoginForm(username, password)
● Does it expose elements or not?
– public WebElement loginButton;
– public WebElement getLoginButton();
– public clickLoginButton();
15. 15
Page Object Functionality
Approaches
● Expose WebElements Directly
– Leads to WebElement Abstractions
– public TextField userNameField;
● Hide WebElements behind physical functional
methods e.g. typeUserName("bob");
● Logical helper methods e.g.
loginAs(name,pass)
● Layers of Page Objects
– Physical
– Logical
16. 16
Navigation Design Decisions
● Does a Page Object return other pages?
public IssueListPage submit(){
driver.findElement(By.id(“submit”)).click();
return new IssueListPage(driver);
}
● Or Navigate implicitly after interactions
● Or have navigation objects
20. 20
POJO
● 'ApplicationPage.java'
– Used in 'SequentialCreationOfTest.java'
– Not much refactoring in the example
● Simple Class
● WebDriver passed to constructor
● Composition of any components
● e.g.
– com.seleniumsimplified.todomvc.page.pojo
– 'ApplicationPage.java'
● Not much refactoring in the example
23. 23
Functional Vs Structural Example
● One way of answering “what methods to put on
a page object”
– Is it functional / behavioural?
– Is it structural / Physical?
● Functional 'uses' Structural implementation
● Why?
– Sometimes it is just a naming difference
– Handling exceptions in functional but not structural
– Higher level methods in Functional
– Different concepts e.g. deleteLastItem
24. 24
Page Factory
● Annotate fields with @FindBy
● Instantiate in constructor using
PageFactory.initElements
– Can create your own page factory initialiser
● Avoids boilerplate accessor methods
● Fast to create Page Objects
● Might become harder to expand and maintain
26. 26
SlowLoadableComponent
● Some pages and components need
synchronisation to wait till they are ready
– One approach – SlowLoadableComponent adds
responsibility for waiting, to the page
● extend SlowLoadableComponent
● Standard interface for synchronisation on load
– get()
● If isLoaded then return this Else load
● While not loaded{ wait for 200 millseconds}
– Implement load and isLoaded
27. 27
Fluent Page Objects
todoMVC = new ApplicationPageFunctionalFluent(
driver, todoMVCSite);
todoMVC.get();
todoMVC.enterNewToDo("First Completed Item").
and().
toggleCompletionOfLastItem().
then().
enterNewToDo("Still to do this").
and().
enterNewToDo("Still to do this too").
then().
filterOnCompleted();
28. 28
Fluent Page Objects
● Methods return the page object or other objects
instead of void
– void clickDeleteButton();
– PageObject clickDeleteButton();
● Syntactic sugar methods:
– and(), then(), also()
● Can work well at high levels of abstraction and
when no navigation involved
● Train Wreck?
29. Navigation Options
29
● Direct in Test: page.get(); page.waitFor();
● Instantiate new pages based on test flow
– Navigation as side-effect, may have to bypass 'get'
– Loadable pages, non-loadable, support classes
● Page Object methods might return other Pages
– e.g. a method on the login page might be
● MyAccountPage clickLogin();
● We might use navigation objects
– direct, Jump.to(MyAccountPage.class)
– path based (current page → desired page)
● Navigate.to(MyAccountPage.class)
30. 30
Possible Domain Abstractions
● Logical Objects
– ToDo
– ToDoList
● Physical Objects
– LocallyStoredToDo
● Actors
– User
● Environment
– Site
– Implementation
31. 31
Page Objects & Domain Objects
● Instead of
– todoMVC.enterNewToDo(“New Item”)
● We could have have
– ToDoItem newItem = new ToDoItem(“New Item”);
– todoMVC.enterNewToDo(newItem);
● See code in DomainBasedTest
32. 32
Domain Objects That Span Logical
& Physical
e.g. User
● user.createNewToDo(“new item”)
● user.createNewToDo(newItem)
● See use in NoAbstractionTest
33. 33
Sometimes it is possible to over
think this stuff
● Don't let thinking about this slow you down
● Conduct experiments
● Refactor
● Rethink if
– you are maintaining too much
– your abstraction layer stops you doing stuff
– you are 'working around' your abstraction layers
35. 35
Element Abstraction Example
public interface Checkbox {
public boolean isChecked();
public Checkbox check();
public Checkbox uncheck();
public Checkbox toggle();
}
● Would you include 'toggle'?
https://xp-dev.com/svn/AutomationAbstractions
36. 36
Element Abstractions
● Existing support classes: Select,
● Possible: TextBox, Checkbox, TextBox, File etc.
● Can enforce Semantics
– Checkbox: isChecked, check(), uncheck(), toggle()
– TextBox: clear(), enterText()
– etc.
● Pass back from Page Objects into test?
37. Element Abstraction Pros and Cons
37
● May have to create a custom page factory
● Can help 'restrict' code i.e. check or uncheck,
rather than click, enforces 'semantics'
● If you create them...
– allow return WebElement
● so that I can go beyond the abstraction layer if I need to.
Not required if it is just a WebElement wrapper.
https://xp-dev.com/svn/AutomationAbstractions
38. 38
Component Abstractions
● Shared Components/Widgets on the page
● e.g.
– VisibleToDoEntry, VisibleToDoList
– Filters, Footer, Header, etc.
● Could have 'functional' representation for
repeated items e.g. login forms
● Could have 'structural' representation
● Likely use : page object composition
39. 39
Component Abstraction Example
TodoEntry todo = page.getToDoEntryAt(lastItemPosition);
todo.markCompleted();
Instead of
page.markTodoCompleted(lastItemPosition);
40. 40
Gherkin as an abstraction layer
Feature: We can create and edit To Do lists in ToDoMvc
We want to amend todos in ToDoMVC because that is
the set of exercises on the abstraction tutorial
Scenario: Create a ToDo Item
Given a user opens a blank ToDoMVC page
When the user creates a todo "new task"
Then they see 1 todo item on the page
● Implement steps using highest appropriate
abstraction layer
● CucumberJVM as 'DSL implementor'
● 'Expressibility' vs 'Step Re-use'
● See todomvc.feature and ToDoMvcSteps
41. 41
My modeling biases
● Driver
– Inject so instantiate any page or component as
required/desired at any time
● Explicit Synchronisation
– To make sure that the desired object is available
and ready for use (as defined by synchronisation)
● Navigation
– Implicit (via taking action e.g. click)
– Explicit Navigation Object, not in page object
● Open/jump (via driver.get)
● To (state model from current, to desired)
42. 42
My modeling biases
● Page Objects
– Physical
● abstract the 'real world'
– Components
● For common features on the page
– Logical
● abstract the functionality
● Sometimes these are entity methods not page objects
– e.g. user.registers().and().logsin()
● I tend not to....
– abstract WebElements
43. 43
My modeling biases
● I tend not to....
– abstract WebElements
– Use inheritence to create a model of the app
● e.g. MyAppPage extends GenericAppPage
– Use 3rd party abstractions on top of WebDriver
44. 44
Are there rights and wrongs?
● Right / Wrong?
● Decisions?
● Project/Team/Organisation Standards?
Identify your own biases -
Experiment
Recognise your decisions
45. 45
Decisions
● The 'limits' and overlap of Abstraction Layers
● Build it now, or 'refactor to' later
● How much change is anticipated?
– To which layer? GUI, Business domain, Workflow?
● Who is working with the automation code?
– Skill levels? Support needed?
● How/When with the automation execute?
46. 46
Other Useful Links
● Jeff “Cheezy” Morgan – page-object ruby gem,
data_magic gem and stareast code
– https://github.com/cheezy?tab=repositories
● Marcus Merrell
– Self-Generating Test Artifacts for Selenium/WebDriver
– https://www.youtube.com/watch?v=mSCFsUOgPpw
● Anand Ramdeo
– One Step at a Time
– https://www.youtube.com/watch?v=dFPgzH_XP1I
https://xp-dev.com/svn/AutomationAbstractions
47. 47
Homework
● Using the code at
https://xp-dev.com/svn/AutomationAbstractions/
– Compare the different implementations under 'main'
● com.seleniumsimplified.todomvc.page
– Investigate how the Page Objects delegate to each
other, and the Domain Objects use Page Objects
– Examine the 'test' usage of the Page Objects and
Domain Objects
– Examine the different navigation approaches
48. 48
Blogs and Websites
● CompendiumDev.co.uk
● SeleniumSimplified.com
● EvilTester.com
● JavaForTesters.com
● Twitter: @eviltester
Online Training Courses
● Technical Web Testing 101
Unow.be/at/techwebtest101
● Intro to Selenium
Unow.be/at/startwebdriver
● Selenium 2 WebDriver API
Unow.be/at/webdriverapi
Videos
youtube.com/user/EviltesterVideos
Books
Selenium Simplified
Unow.be/rc/selsimp
Java For Testers
leanpub.com/javaForTesters
Alan Richardson
uk.linkedin.com/in/eviltester
Independent Test Consultant
& Custom Training
Contact Alan
http://compendiumdev.co.uk/contact