1. The Page Object Pa-ern
A
basic
DRY
abstrac-on
pa1ern
for
Web
browser
automa-on
test
development,
maintenance
and
versioning
Alex
Kogon
alex@kogon.com
2. Basic Web Test Development
• What
do
we
do
in
Selenium
Web
Test
Development?
Just
like
the
user,
we
launch
a
Web
browser,
load
pages,
and
read,
write,
and
click
on
bu1ons
and
links.
• How
do
we
interact
with
the
Web
browser?
The
Web
browser
is
interface
via
an
Object
in
your
code,
which
can
be
told
to
load
pages,
and
queried
for
Element
Objects
that
are
currently
on
the
displayed
Web
page.
These
Element
Objects
may
be
read
from,
wri1en
to,
or
clicked
on.
3. A First Basic Selenium Script
1. Load
www.google.com
2. Find
the
search
text
field
3. Enter
“Alex
Kogon”
4. Find
the
search
bu1on
5. Click
on
it
4. A First Basic Selenium Script (Java)
public
class
BasicWebTest
{
private
WebDriver
theSeleniumWebDriver;
@Before
public
void
setUp()
throws
Exception
{
theSeleniumWebDriver
=
new
RemoteWebDriver(new
URL("http://localhost:4444/wd/hub"),
DesiredCapabilities.firefox());
}
@Test
public
void
test()
{
theSeleniumWebDriver.get("http://www.google.com");
final
WebElement
myGoogleInputElement
=
theSeleniumWebDriver.findElement(By.cssSelector("#lst-‐ib"));
myGoogleInputElement.sendKeys("Alex
Kogon");
final
WebElement
mySearchButtonElement
=
theSeleniumWebDriver.findElement(By.cssSelector("#tsf
>
div.tsf-‐p
>
div.jsb
>
center
>
input[type="submit"]:nth-‐child(1)"));
mySearchButtonElement.click();
}
}
5. Adding more searches
1. Load
www.google.com
2. Find
the
search
text
field
3. Enter
other
varia-ons
on
name
(“Alexander
Kogon”,
“Kogon,
Alex”)
4. Find
the
search
bu1on
5. Click
on
it
6. Another Search
@Test
public
void
anotherTest()
{
theSeleniumWebDriver.get("http://www.google.com");
final
WebElement
myGoogleInputElement
=
theSeleniumWebDriver.findElement(By.cssSelector("#lst-‐ib"));
myGoogleInputElement.sendKeys("Kogon,
Alex");
final
WebElement
mySearchButtonElement
=
theSeleniumWebDriver.findElement(By.cssSelector("#tsf
>
div.tsf-‐p
>
div.jsb
>
center
>
input[type="submit"]:nth-‐
child(1)"));
mySearchButtonElement.click();
}
7. Et cetera…
@Test
public
void
yetAnotherTest()
{
theSeleniumWebDriver.get("http://www.google.com");
final
WebElement
myGoogleInputElement
=
theSeleniumWebDriver.findElement(By.cssSelector("#lst-‐ib"));
myGoogleInputElement.sendKeys("Alexander
Kogon");
final
WebElement
mySearchButtonElement
=
theSeleniumWebDriver.findElement(By.cssSelector("#tsf
>
div.tsf-‐p
>
div.jsb
>
center
>
input[type="submit"]:nth-‐child(1)"));
mySearchButtonElement.click();
}
@Test
public
void
andSoAnotherTest()
{
theSeleniumWebDriver.get("http://www.google.com");
final
WebElement
myGoogleInputElement
=
theSeleniumWebDriver.findElement(By.cssSelector("#lst-‐ib"));
myGoogleInputElement.sendKeys("Kogon,
Alexander");
final
WebElement
mySearchButtonElement
=
theSeleniumWebDriver.findElement(By.cssSelector("#tsf
>
div.tsf-‐p
>
div.jsb
>
center
>
input[type="submit"]:nth-‐child(1)"));
mySearchButtonElement.click();
}
8. Eureka!
Isn’t
this
great?
With
almost
no
effort
we
can
write
test
scripts
building
new
func-onality
from
what
we’ve
already
done
by
copying
and
pas-ng
the
exis-ng
work
we’ve
done
and
modifying
a
couple
things.
9. So what’s the problem?
How
about
if
something
changes?
By
building
a
test
from
an
exis-ng
site
and
copying
the
CSS
selectors,
it
is
easy
to
build
a
large
body
of
func-onality
tests.
However,
what
happens
if
the
CSS
selectors
(or
other
underlying
func-onality)
change?
10. Bri-le Selectors
Please
give
us
a
unique
selector!
When
construc-ng
selectors,
there
are
a
variety
of
op-ons
on
how
it
may
be
done.
The
best
selectors
reference
unique
iden-fiers
associated
with
the
HTML
element,
like
the
Google
Search
Text
Field
in
our
example
code:
theSeleniumWebDriver.findElement(By
.cssSelector("#lst-‐ib"));
However,
quite
oben
the
developers
have
not
inserted
unique
tags,
and
we
get
a
selector
similar
to
what
we
used
for
the
Google
Search
Bu1on
in
our
example
code:
final
WebElement
mySearchButtonElement
=
theSeleniumWebDriver
.findElement(By
.cssSelector("#tsf
>
div.tsf-‐p
>
div.jsb
>
center
>
input[type="submit"]:nth-‐child(1)"));
11. What Happens When Selectors Change?
Layout
based
selectors
are
a
headache…
Look
at
the
second
selector
again:
cssSelector("#tsf
>
div.tsf-‐p
>
div.jsb
>
center
>
input[type="submit"]:nth-‐child(1)"));
With
a
bit
of
knowledge
of
CSS
selectors,
you
can
see
that
this
is
querying
a
subsec-on
of
the
page
for
all
of
it’s
submit
bu1ons,
and
asking
for
the
first
one.
What
if
the
layout
of
the
page
changed,
or
there
was
another
bu1on
inserted
before
it?
Suddenly
either
no
bu1on
will
be
found
or
the
wrong
one
clicked.
Ideally,
the
developers
can
be
convinced
to
insert
unique
selectors
into
every
element
you
need
to
access,
but
oben
this
is
not
the
case.
How
to
handle
the
changes?
12. Brute Force
Search
and
Replace
So
let’s
assume
the
guys
at
Google
put
in
another
bu1on
before
the
search
bu1on.
Our
CSS
selector
now
looks
like:
cssSelector("#tsf
>
div.tsf-‐p
>
div.jsb
>
center
>
input[type=
"submit"]:nth-‐child(2)"));
OK,
well
we’ve
got
four
uses
of
this
selector,
but
they
are
all
in
the
same
file,
so
searching
and
replacing
is
not
the
end
of
the
world.
What
about
if
we
had
hundreds?
And
they
were
in
different
files?
What
if
we
just
had
to
change
it
once?
While
copying
and
pas-ng
code
all
over
is
easy
and
convenient
for
reuse,
it
creates
a
lot
of
extra
busy
work
when
something
needs
to
be
changed.
By
crea-ng
a
“Single
Point
Of
Truth”,
or
a
single
way
in
which
this
selector
is
accessed,
we
only
need
to
change
it
once.
A
bit
of
extra
work
up
front
to
save
a
lot
of
work
in
the
future.
13. Don’t Repeat Yourself (DRY)
Encapsulate
code
in
a
single
loca-on
OK,
so
we’ve
copied
the
same
code
in
four
places,
and
now
we
need
to
change
it.
To
do
so
most
easily,
we
would
like
to
only
have
to
change
it
once.
What
do
we
do?
Refactor.
One
of
the
most
powerful
innova-ons
in
modern
IDE’s
is
the
ability
to
automa-cally
have
the
IDE
change
code,
a.k.a.
“refactoring”.
Refactoring
allows
you
to
change
the
architecture
of
your
code
on
the
fly,
adop-ng
more
complicated
structures
as
demanded,
while
keeping
things
as
simple
as
possible
at
the
-me.
There
are
several
possible
solu-ons
for
how
to
refactor
this
code
to
be
DRY;
we
will
work
with
extrac-ng
a
common
method
that
is
accessed
from
all
the
tests.
14. What if the Selector is used across mulOple
script files?
Share
methods
within
a
u-lity
class
So
now
we’ve
created
a
single
method
containing
the
CSS
Selector
that
can
be
easily
modified
to
update
the
Selector
when
it
changes.
However,
that
method
is
only
in
one
class.
If
the
selector
is
used
from
mul-ple
test
script
class
files,
the
change
must
be
implemented
in
each
one.
Wouldn’t
it
be
easier
to
just
move
the
method
to
another
class
where
it
could
be
shared
by
all
the
different
test
scripts?
The
“Move”
refactor
is
also
quite
helpful
in
doing
this.
15. The Page Object Pa-ern
Break
your
u-lity
classes
out
by
Page
The
u-lity
class
we
have
just
created
contains
all
the
logic
to
access
elements
on
the
Web
pages
we
use
in
our
test.
Assuming
there
are
many
Web
pages,
they
are
all
mixed
together
in
our
class.
By
breaking
out
the
u-lity
class
into
separate
classes,
each
with
the
accessors
for
a
single
Web
page,
we
create
a
be1er
abstrac-on
where
we
can
easily
share
func-onality
across
our
test
suite
by
bringing
in
(and
extending)
exis-ng
Page
Object
code
for
each
page
a
test
uses.
16. Code
Page Object Pa-ern Architecture
Google
Home
Page
Google
Search
Result
Page
Google
Home
Page
Object
Google
Search
Result
Page
Object
Page Objects
Web Pages
Test
1
Test
2
Test
3
Test Scripts
17. Maven and Page Objects
Store
each
page
as
a
library
in
Maven
Now
that
we’ve
broken
up
our
accessors
into
Page
Objects
so
they
can
be
easily
shared
across
test
scripts,
why
not
put
them
into
a
separate
module
so
that
they
can
be
easily
shared
across
test
projects
as
well?
By
pugng
each
Page
Object
into
a
unique
Maven
module,
tests
can
simply
define
all
the
Page
Objects
they
need
to
reference
in
their
Maven
configura-on
(pom.xml)
file,
and
have
access
to
the
Page
Objects
without
needing
to
copy
the
Page
Objects
into
mul-ple
projects
(another
DRY
viola-on)
or
have
them
all
in
a
single
project.
18. Maven, Page Objects, and Versioning
Added
benefits
of
using
Maven
OK,
now
we’ve
encapsulated
each
of
our
CSS
selectors
in
a
single
accessor
method,
stored
each
of
these
in
a
unique
Object
represen-ng
each
Web
page
used,
and
created
a
Maven
library
for
this
Page
Object.
What
next?
When
running
Web
automa-on
tes-ng
in
a
large,
dynamic,
organiza-on,
you
will
find
quite
oben
that
there
are
mul-ple
versions
of
each
Web
page
that
must
be
tested
simultaneously.
Reliability
tes-ng
on
the
live
site
requires
the
version
currently
in
live
is
tested;
integra-on
tes-ng
on
imminent
releases
require
the
version
ready
for
release;
and
development
tes-ng
on
new
versions
of
the
Web
page
(perhaps
a
pipeline
of
mul-ple
future
releases)
require
the
version
for
each
of
those
pages.
Luckily
Maven
already
provides
us
with
a
solu-on.
Maven
libraries
are
easily
versioned
for
deployment,
such
that
many
versions
of
the
library
can
be
available
for
use
by
the
tests
depending
on
which
version
of
each
page
is
to
be
tested.
A
single
test
script
is
s-ll
able
to
be
used
on
the
various
versions,
as
the
differences
in
the
implementa-on
in
each
version
is
abstracted
behind
the
Page
Object.
19. Code
Page Object Pa-ern Versioning
Google
Home
Page
0.0.1
Google
Home
Page
0.0.2
Google
Home
Page
Object
V
0.0.1
Google
Home
Page
Object
V
0.0.2
Page Objects
Web Pages
Test
1
Test
2
Test
3
Test Scripts
20. ConOnuous Deployment and IntegraOon
The
full
enchilada
Now
that
we’ve
got
reusable,
versioned
Page
Objects
referenced
from
our
Web
script
code
via
Maven,
and
automated
tests
which
leverage
the
page
objects
by
version,
let’s
see
how
this
works
in
our
Automa-on
solu-ons.
A
Con-nuous
Deployment
and
Integra-on
system
can
be
easily
leveraged
to
provide
tes-ng
across
all
the
mul-ple
versions
in
real
-me.
By
allowing
the
defini-on
of
the
version
of
each
page
to
be
deployed
for
each
tes-ng
to
be
defined,
the
correct
version
of
each
Page
can
be
deployed
into
the
Web
applica-on
servers
for
the
test
run,
and
the
correct
version
of
each
Page
Object
corresponding
to
that
Page
version
used
(by
the
iden-cal
test)
by
dynamically
instruc-ng
Maven
to
use
the
same
version
of
the
Page
Object
that
was
just
deployed
for
tes-ng.
In
this
way
it
is
very
easy
to
test
mul-ple
integra-on
scenarios
in
real
-me
(produc-on,
integra-on,
development,
etc.),
and
to
easily
test
any
possible
integra-on
of
versions
by
simply
defining
the
versions
to
be
used
for
an
Automa-on
run.
Try
doing
that
with
hard
coding.
21. ConOnuous IntegraOon with Versioning
Jenkins
ConOnuous
IntegraOon
Server
Trigger
0.0.1
Build
Checkout
0.0.1
Branch
Git
Version
Control
Server
Build
0.0.1
Branch
Nexus
Maven
Deployment
Server
Tomcat
Web
ApplicaOon
Server
Deploy
0.0.1
Branch
Run
0.0.1
Test
Maven
Test
Runner
Run
With
0.0.1
Page
Object
Selenium
Test
Run
Firefox
Drive
Browser
Deliver
0.0.1
Page
Objects
Deliver
0.0.1
Pages