1
GEB FOR TESTING
YOUR GRAILS
APPLICATION
Jacob Aae Mikkelsen
2
SURVEY AND RULES
AGENDA
Functional testing
Geb - cudos and history
How Geb works
Geb and Grails 2 and 3
Browser support
Javascript
3 . 1
JACOB AAE MIKKELSEN
Senior Engineer at LEGO
Microservice based architechture on JVM
Previously 4 years at Gennemtænkt IT
Consultant on Groovy and Grails
External Associate Professor - University of Southern
Denmark
@JacobAae
Blogs The Grails Diary
3 . 24 . 1
FUNCTIONAL TESTING
4 . 2
WHY?
4 . 3
4 . 4
FUNCTIONAL TESTING
Ignores the specifics of the underlying software
component under test.
Whitebox / Greybox
Merely asserts that providing certain input results in
certain output.
Web-application: Programmatically controlling a web
browser to simulate the actions of a user on a web page.
4 . 5
BROWSER AUTOMATION
4 . 64 . 7
PAIN
Traditionally been tedious, cumbersome and brittle
to do

Geb helps ease the pain
4 . 8
GAIN
Any browser based application can be tested
5 . 1
GEB
5 . 2
GEB HISTORY
Started in November 2009
Created by Luke Daley
Current project lead Marcin Erdman
WHY GEB
jQuery like selector syntax
Power of WebDriver (Easier api)
Robustness of Page Object modeling
Expressiveness of the Groovy language
Integrates well with build systems (Gradle/Maven)
Excellent user manual/documentation
5 . 3
GEB IMPLEMENTATION
Build on top of the WebDriver browser automation library
successor to the Selenium Remote Control (RC) testing
framework.
Selenium RC → JavaScript to interact
WebDriver → native browser drivers
Use JUnit or Spock
5 . 4
WEBDRIVER
Very active development
Stable API and feature set
Verbose
Low level
Not a complete solution
5 . 5
6 . 1
USING GEB
5 . 6
6 . 2
NAVIGATOR
The $() method returns a Navigator object
General format
$ ( < c s s s e l e c t o r > , < i n d e x / r a n g e > , < a t t r i b u t e / t e x t m a t c h e r s > )
GEB SELECTORS (1)
Jquery like selecter syntax
/ / m a t c h a l l ' p ' e l e m e n t s o n p a g e
$ ( " p " )
/ / m a t c h t h e f i r s t ' p ' e l e m e n t o n t h e p a g e
$ ( " p " , 0 )
/ / A l l ' d i v ' e l e m e n t s w i t h a t i t l e v a l u e ' s e c t i o n '
$ ( " d i v " , t i t l e : " s e c t i o n " )
/ / m a t c h t h e f i r s t ' d i v ' e l e m e n t t e x t ' s e c t i o n '
$ ( " d i v " , 0 , t e x t : " s e c t i o n " )
/ / m a t c h t h e f i r s t ' d i v ' e l e m e n t w i t h t h e c l a s s ' m a i n '
$ ( " d i v . m a i n " , 0 )
6 . 3
GEB SELECTORS (2)
Text attribute supports regex
/ / A n y d i v w i t h t h e t e x t s t a r t i n g w i h G R 8
$ ( " d i v " , t e x t : ~ / G R 8 . + / )
$ ( " p " , t e x t : s t a r t s W i t h ( " G R 8 " ) )
/ / A n d o t h e r h a n d y p r e d i c a t e s
$ ( " d i v " , c l a s s : c o n t a i n s ( " u i - " ) )
6 . 4
GEB SELECTORS (3)
Selecting returns Navigatorobjects
/ / T h e p a r e n t o f t h e f i r s t d i v
$ ( " d i v " , 0 ) . p a r e n t ( )
/ / A l l t a b l e s w i t h a c e l l s p a c i n g
/ / a t t r i b u t e v a l u e o f 0 t h a t a r e n e s t e d i n a p a r a g r a p h
$ ( " p " ) . f i n d ( " t a b l e " , c e l l s p a c i n g : ' 0 ' )
6 . 56 . 6
CSS SUPPORT
$ ( " t a b l e t r : n t h - c h i l d ( 2 n + 1 ) t d " )
6 . 7
RETRIVING INFORMATION
$ ( " p " ) . t e x t ( ) = = " a "
$ ( " p " ) . t a g ( ) = = " p "
$ ( " p " ) . @ t i t l e = = " a "
$ ( " p " ) . c l a s s e s ( ) = = [ " a " , " p a r a " ]
INTERACTION WITH CONTENT
click()
isDisplayed()
withConfirm{}
withAlert{}
$ ( " a . b t n " ) . c l i c k ( )
$ ( " d i v " ) . i s D i s p l a y e d ( )
w i t h C o n f i r m {
$ ( " b u t t o n . d e l e t e " ) . c l i c k ( )
}
6 . 86 . 9
SENDING INPUT
i m p o r t o r g . o p e n q a . s e l e n i u m . K e y s
/ / S h o r t h a n d f o r s e n d K e y s ( ) m e t h o d o f W e b D r i v e r .
$ ( " d i v " ) < < " a b c "
$ ( " i n p u t " , n a m e : " f o o " ) < < K e y s . c h o r d ( K e y s . C O N T R O L , " c " )
6 . 10
INTERACTION
Using ActionsAPI from WebDriver is possible.
But Geb provides the interactclosure
6 . 11
CONTROL-CLICKING
i m p o r t o r g . o p e n q a . s e l e n i u m . K e y s
i n t e r a c t {
k e y D o w n K e y s . C T R L
c l i c k $ ( " a . m y L i n k " )
k e y U p K e y s . C T R L
}
SIMULATE DRAG-N-DROP
i n t e r a c t {
c l i c k A n d H o l d ( $ ( ' # d r a g g a b l e ' ) )
m o v e B y O f f s e t ( 1 5 0 , 2 0 0 )
r e l e a s e ( )
}
Or easier
i n t e r a c t {
d r a g A n d D r o p B y ( $ ( " # d r a g g a b l e " ) , 1 5 0 , 2 0 0 )
}
6 . 12
MORE INTERACTION WITH FORMS
Consider the following HTML…
< f o r m >
< i n p u t t y p e = " t e x t " n a m e = " g e b " v a l u e = " F u n c t i o n a l " / >
< / f o r m >
The value can be read and written via property notation…
$ ( " f o r m " ) . g e b = = " F u n c t i o n a l "
$ ( " f o r m " ) . g e b = " T e s t i n g "
$ ( " f o r m " ) . g e b = = " T e s t i n g "
These are literally shortcuts for…
$ ( " f o r m " ) . f i n d ( " i n p u t " , n a m e : " g e b " ) . v a l u e ( ) = = " F u n c t i o n a l "
$ ( " f o r m " ) . f i n d ( " i n p u t " , n a m e : " g e b " ) . v a l u e ( " T e s t i n g " )
$ ( " f o r m " ) . f i n d ( " i n p u t " , n a m e : " g e b " ) . v a l u e ( ) = = " T e s t i n g "
6 . 13
VARIABLES AVAILABLE
title
browser
currentUrl
currentWindow
6 . 14
MORE POSSIBILITIES
Uploading files
Downloading files
Interacting with javascript
js object (Example later)
6 . 157 . 1
STANDALONE GEB SCRIPT
GEB STANDALONE EXAMPLE
Lets try to automate:
Searching for GR8Conf India
Click the first link
Hopefully end up on the right homepage
7 . 2
GEB STANDALONE EXAMPLE
g o " h t t p : / / d u c k d u c k g o . c o m "
$ ( ' i n p u t ' , n a m e : ' q ' ) . v a l u e ( " G R 8 C o n f I n d i a " )
$ ( ' i n p u t ' , n a m e : ' q ' ) < < K e y s . E N T E R
w a i t F o r ( 1 0 , 1 ) { $ ( " # l i n k s " ) . d i s p l a y e d }
s l e e p ( 3 0 0 0 ) / / F o r d e m o r e a s o n s
$ ( " h 2 . r e s u l t _ _ t i t l e " ) . f i r s t ( ) . c l i c k ( )
w a i t F o r { t i t l e = " G R 8 C o n f I N - 2 0 1 6 " }
7 . 38 . 1
STRUCTURING GEB TESTS
SCENARIO
Lets test a CRUD part of a grails application registrering
conference attendees
Lets test the following
1. Goto list of attendees page
2. Create new attendee (incl. invalid data once)
3. Update the attendee
4. Check data is updated
8 . 28 . 3
GEB SPEC BASICS
i m p o r t g e b . s p o c k . G e b S p e c
@ S t e p w i s e / / E n s u r e s t h e t e s t s a r e r u n s e q u e n t i a l l y
c l a s s A t t e n d e e F u n c t i o n a l S p e c e x t e n d s G e b S p e c {
/ / S p o c k s p e c s h e r e
}
GEB SPEC (1)
The naive inmaintainable way!
v o i d " G o t o l i s t p a g e - c h e c k i n i t i a l s t a t e " ( ) {
w h e n : " T h e h o m e p a g e i s v i s i t e d "
g o ' / a t t e n d e e / i n d e x '
t h e n :
t i t l e = = " A t t e n d e e L i s t "
}
8 . 4
GEB SPEC (2)
The naive inmaintainable way!
v o i d " C l i c k n e w a t t e n d e e b u t t o n " ( ) {
w h e n :
$ ( " a . c r e a t e " ) . c l i c k ( )
t h e n :
t i t l e = = " C r e a t e A t t e n d e e "
}
8 . 5
GEB SPEC (3)
The naive inmaintainable way!
v o i d " S u b m i t f o r m w i t h e r r o r s " ( ) {
w h e n :
$ ( " b u t t o n . b t n - p r i m a r y " ) . c l i c k ( )
t h e n :
t i t l e = = " C r e a t e A t t e n d e e "
}
8 . 6
GEB SPEC (4)
The naive inmaintainable way!
v o i d " S u b m i t f o r m w i t h n o e r r o r s " ( ) {
w h e n :
$ ( ' f o r m ' ) . n a m e = ' D e e p a k '
$ ( ' f o r m ' ) . e m a i l = ' d e e p a k @ m a i l . o r g '
a n d :
$ ( " b u t t o n . b t n - p r i m a r y " ) . c l i c k ( )
t h e n :
t i t l e = = ' S h o w A t t e n d e e '
$ ( ' d i v . p r o p e r t y - v a l u e ' ) . f i n d { i t . t e x t ( ) = = ' D e e p a k ' }
$ ( ' d i v . p r o p e r t y - v a l u e ' ) . f i n d { i t . t e x t ( ) = = ' d e e p a k @ m a i l . o r g ' }
}
8 . 7
GEB SPEC (5)
The naive inmaintainable way!
v o i d " C l i c k E d i t B u t t o n " ( ) {
w h e n :
$ ( " a . b t n - p r i m a r y " ) . c l i c k ( )
t h e n :
t i t l e = = ' E d i t A t t e n d e e '
}
8 . 8
GEB SPEC (6)
The naive inmaintainable way!
v o i d " U p d a t e A t t e n d e e " ( ) {
w h e n :
$ ( ' f o r m ' ) . n a m e = ' A m i t '
$ ( ' f o r m ' ) . e m a i l = ' a m i t @ m a i l . o r g '
a n d :
$ ( " b u t t o n . b t n - p r i m a r y " ) . c l i c k ( )
t h e n :
t i t l e = = ' S h o w A t t e n d e e '
$ ( ' s p a n . p r o p e r t y - v a l u e ' ) . f i n d { i t . t e x t ( ) = = ' A m i t ' }
$ ( ' s p a n . p r o p e r t y - v a l u e ' ) . f i n d { i t . t e x t ( ) = = ' a m i t @ m a i l . o r g ' }
}
8 . 99 . 1
GEB SPEC - THE BETTER WAY
If we make a few scenarios, there will be
Much duplication
Many places to correct if we change the layout / DOM
9 . 29 . 3
SOLUTION
Use pages and modules
PAGE OBJECTS
Describes a web page
Url
How to check if we are at the correct place
Content we wish to interact with
.. and how it is found
Helper methods
9 . 4
PAGE OBJECTS
i m p o r t e u . g r 8 c o n f . g r a i l s d e m o . m o d u l e s . N a v i g a t i o n B a r M o d u l e
i m p o r t g e b . P a g e
c l a s s A t t e n d e e S h o w P a g e e x t e n d s P a g e {
s t a t i c u r l = " / a t t e n d e e / s h o w "
s t a t i c a t = { t i t l e = = ~ / S h o w A t t e n d e e / }
s t a t i c c o n t e n t = {
a t t P r o p { $ ( ' s p a n . p r o p e r t y - l a b e l ' ) }
n a m e { a t t P r o p . f i n d { i t . t e x t ( ) = = ' N a m e ' } . n e x t ( ) . t e x t ( ) }
e m a i l { a t t P r o . f i n d { i t . t e x t ( ) = = ' E m a i l ' } . n e x t ( ) . t e x t ( ) }
e d i t B u t t o n { $ ( " a . b t n - p r i m a r y " ) }
}
}
9 . 59 . 6
CONTENT CLOSURE
s t a t i c c o n t e n t = {
i n f o ( r e q u i r e d : f a l s e ) { $ ( " d i v . i n f o " ) }
m e s s a g e ( w a i t : f a l s e ) { $ ( " d i v . m e s s a g e " ) }
}
9 . 7
MODULES
Describes repeated content
Across pages
Within the same page
MODULES
i m p o r t g e b . M o d u l e
c l a s s N a v i g a t i o n B a r M o d u l e e x t e n d s M o d u l e {
s t a t i c b a s e = { $ ( ' n a v . n a v b a r ' ) }
s t a t i c c o n t e n t = {
h o m e ( r e q u i r e d : f a l s e ) { $ ( ' a . h o m e ' ) }
l i s t A t t e n d e e ( r e q u i r e d : f a l s e ) { $ ( ' a . l i s t ' ) }
n e w A t t e n d e e ( r e q u i r e d : f a l s e ) { $ ( ' a . c r e a t e ' ) }
}
}
9 . 89 . 9
MODULES
s t a t i c c o n t e n t = {
/ / L i k e t h i s , t h e m o d u l e d o e s n o t n e e d a b a s e
/ / f o r m { m o d u l e N a v i g a t i o n B a r M o d u l e , $ ( ' n a v . n a v b a r ' ) }
f o r m { m o d u l e N a v i g a t i o n B a r M o d u l e }
}
MODULE FOR REPEATED CONTENT
IN A PAGE
i m p o r t g e b . M o d u l e
c l a s s A t t e n d e e L i s t I t e m M o d u l e e x t e n d s M o d u l e {
s t a t i c c o n t e n t = {
d a t a { $ ( " t d " , i t ) }
n a m e { d a t a ( 0 ) . t e x t ( ) }
e m a i l { d a t a ( 1 ) . t e x t ( ) }
n a t i o n a l i t y { d a t a ( 2 ) . t e x t ( ) }
d a t e C r e a t e d { d a t a ( 3 ) . t e x t ( ) }
l a s t U p d a t e d { d a t a ( 4 ) . t e x t ( ) }
}
}
9 . 10
MODULE FOR REPEATED CONTENT
IN A PAGE
AttendeeListPage.groovy
s t a t i c c o n t e n t = {
m e n u b a r { m o d u l e N a v i g a t i o n B a r M o d u l e }
a t t e n d e e s { m o d u l e L i s t A t t e n d e e L i s t I t e m M o d u l e ,
$ ( " t a b l e t r " ) . t a i l ( ) }
}
9 . 119 . 12
MODULE FOR REPEATED CONTENT
IN A PAGE
w h e n :
t o A t t e n d e e L i s t P a g e
t h e n :
a t t e n d e e s * . n a m e . c o n t a i n s ( ' B u r t B e c k w i t h ' )
GEB SPEC - STRUCTURED (1)
Lets try to restructure the ugly spec from before
v o i d " G o t o l i s t p a g e - c h e c k i n i t i a l s t a t e " ( ) {
w h e n :
t o A t t e n d e e I n d e x P a g e
t h e n :
a t A t t e n d e e I n d e x P a g e
}
9 . 139 . 14
GEB SPEC - STRUCTURED (2)
v o i d " C l i c k n e w a t t e n d e e b u t t o n " ( ) {
w h e n :
m e n u b a r . n e w A t t e n d e e . c l i c k ( )
t h e n :
a t A t t e n d e e C r e a t e P a g e
}
9 . 15
GEB SPEC - STRUCTURED (3)
v o i d " S u b m i t f o r m w i t h e r r o r s " ( ) {
w h e n :
s u b m i t B u t t o n . c l i c k ( )
t h e n :
a t A t t e n d e e C r e a t e P a g e
}
GEB SPEC - STRUCTURED (4)
v o i d " S u b m i t f o r m w i t h n o e r r o r s " ( ) {
w h e n :
f o r m . n a m e = ' D e e p a k '
f o r m . e m a i l = ' d e e p a k @ m a i l . o r g '
a n d :
s u b m i t B u t t o n . c l i c k ( )
t h e n :
a t A t t e n d e e S h o w P a g e
n a m e = = ' D e e p a k '
e m a i l = = ' d e e p a k @ m a i l . o r g '
}
9 . 169 . 17
GEB SPEC - STRUCTURED (5)
v o i d " C l i c k E d i t B u t t o n " ( ) {
w h e n :
e d i t B u t t o n . c l i c k ( )
t h e n :
a t A t t e n d e e E d i t P a g e
}
GEB SPEC - STRUCTURED (6)
v o i d " U p d a t e A t t e n d e e " ( ) {
w h e n :
f o r m . n a m e = ' A m i t '
f o r m . e m a i l = ' a m i t @ s o m e m a i l . c o m '
a n d :
u p d a t e B u t t o n . c l i c k ( )
t h e n :
a t A t t e n d e e S h o w P a g e
n a m e = = ' A m i t '
e m a i l = = ' a m i t @ s o m e m a i l . c o m '
}
9 . 18
STANDALONE REVISITED
t o D u c k D u c k G o P a g e
i n p u t F i e l d < < " G R 8 C o n f I n d i a "
s u b m i t ( )
w a i t F o r ( 1 0 , 0 . 5 ) {
a t D u c k D u c k G o R e s u l t P a g e
}
s l e e p ( 3 0 0 0 ) / / F o r d e m o r e a s o n s
c l i c k L i n k ( 0 )
w a i t F o r {
a t G R 8 C o n f I n d i a P a g e
}
9 . 19
STANDALONE REVISITED
c l a s s D u c k D u c k G o P a g e e x t e n d s g e b . P a g e {
s t a t i c u r l = " h t t p : / / d u c k d u c k g o . c o m "
s t a t i c a t = { t i t l e = = ~ / D u c k D u c k G o / }
s t a t i c c o n t e n t = {
i n p u t F i e l d { $ ( ' i n p u t ' , n a m e : ' q ' ) }
}
d e f s u b m i t ( ) {
i n p u t F i e l d < < K e y s . E N T E R
}
}
9 . 2010 . 1
GEB WITH GRAILS
GEB AND GRAILS 2.X
Must install plugin in BuildConfig.groovy
d e p e n d e n c i e s {
. . .
t e s t ( " o r g . s e l e n i u m h q . s e l e n i u m : s e l e n i u m - s u p p o r t : 2 . 4 5 . 0 " )
t e s t ( " o r g . s e l e n i u m h q . s e l e n i u m : s e l e n i u m - f i r e f o x - d r i v e r : 2 . 4 5 . 0 " )
t e s t " o r g . g e b i s h : g e b - s p o c k : 0 . 1 0 . 0 "
}
p l u g i n s {
. . .
t e s t " o r g . g r a i l s . p l u g i n s : g e b : 0 . 1 0 . 0 "
}
10 . 210 . 3
GEB TESTS IN GRAILS 2.X
Tests placed in test/functionalfolder
Running the tests
g r a i l s t e s t - a p p f u n c t i o n a l :
GEB AND GRAILS 3
Geb is default in build.gradle
d e p e n d e n c i e s {
. . .
t e s t C o m p i l e " o r g . g r a i l s . p l u g i n s : g e b "
/ / N o t e : I t i s r e c o m m e n d e d t o u p d a t e t o a m o r e r o b u s t d r i v e r
/ / ( C h r o m e , F i r e f o x e t c . )
t e s t R u n t i m e ' o r g . s e l e n i u m h q . s e l e n i u m : s e l e n i u m - h t m l u n i t - d r i v e r : 2 . 4 4 . 0
}
10 . 4
GEB TESTS IN GRAILS 3
Creating Geb Spec
g r a i l s c r e a t e - f u n c t i o n a l - t e s t M y G e b S c e n a r i o
Placing the test in src/integration-test/groovy
Running the tests
g r a i l s t e s t - a p p - i n t e g r a t i o n
10 . 5
GENERATED CLASS
@ I n t e g r a t i o n
@ R o l l b a c k
c l a s s M a n y A t t e n d e e s S p e c e x t e n d s G e b S p e c {
v o i d " t e s t s o m e t h i n g " ( ) {
w h e n : " T h e h o m e p a g e i s v i s i t e d "
g o ' / '
t h e n : " T h e t i t l e i s c o r r e c t "
$ ( ' t i t l e ' ) . t e x t ( ) = = " W e l c o m e t o G r a i l s "
}
}
10 . 610 . 7
INTERACTING WITH APPLICATION
When some functionality is needed that is not exposed
through the browser, it can be necessary to interact with the
application under test.
GRAILS 2.5
Tests not running in same JVM
Done with remote-control plugin
Send a closure for execution in application
c o m p i l e " : r e m o t e - c o n t r o l : 2 . 0 "
10 . 810 . 9
REMOTE CONTROL
s e t u p : ' C r e a t e s o m e i t e m n o t a v a i l a b l e t h r o u g h G U I '
d e f i d = r e m o t e {
I t e m i t e m = n e w I t e m ( n a m e : " M y I t e m " )
i t e m . s a v e ( )
i t e m . i d
}
10 . 10
GRAILS 3.0
Application is in same jvm
Interaction is possible directly
Tests run as integration tests
INTERACTING WITH APPLICATION
v o i d " T e s t P a g i n a t i o n i s s h o w n w i t h 1 5 a t t e n d e e s " ( ) {
s e t u p :
A t t e n d e e . w i t h N e w T r a n s a c t i o n {
1 5 . t i m e s {
n e w A t t e n d e e ( n a m e : " N $ i t " , e m a i l : " m $ i t @ t . d k " ) . s a v e ( )
}
}
w h e n :
t o A t t e n d e e I n d e x P a g e
t h e n :
h a s P a g i n a t i o n ( )
}
10 . 1110 . 12
INTERACTING WITH APPLICATION
s t a t i c c o n t e n t = {
m e n u b a r { m o d u l e N a v i g a t i o n B a r M o d u l e }
p a g i n a t i o n ( r e q u i r e d : f a l s e ) { $ ( ' s p a n . c u r r e n t S t e p ' ) }
}
b o o l e a n h a s P a g i n a t i o n ( ) {
p a g i n a t i o n . t e x t ( )
}
11 . 1
CONFIGURATION AND BROWSER
SUPPORT
11 . 2
GEBCONFIG
Configuration for Geb is placed in GebConfig.groovy
In Grails 3, place it in ´src/integration-test/groovy`
 http://www.gebish.org/manual/current/configuration.htm
11 . 3
DRIVER
It is possible to configure the browser used.
SUPPORTED DRIVERS
Firefox
Chrome
InternetExplorer
Safari
HtmlUnit
PhantomJS
11 . 411 . 5
DRIVER
It is possible to configure the browser used.
build.gradle
c o m p i l e ' o r g . s e l e n i u m h q . s e l e n i u m : s e l e n i u m - c h r o m e - d r i v e r : 2 . 4 9 . 0 '
c o m p i l e ' o r g . s e l e n i u m h q . s e l e n i u m : s e l e n i u m - f i r e f o x - d r i v e r : 2 . 4 9 . 0 '
FIREFOX
GebConfig.groovy
i m p o r t o r g . o p e n q a . s e l e n i u m . f i r e f o x . F i r e f o x P r o f i l e
i m p o r t o r g . o p e n q a . s e l e n i u m . f i r e f o x . F i r e f o x D r i v e r
d r i v e r = {
F i r e f o x P r o f i l e p r o f i l e = n e w F i r e f o x P r o f i l e ( )
p r o f i l e . s e t P r e f e r e n c e ( " b r o w s e r . d o w n l o a d . f o l d e r L i s t " , 2 )
p r o f i l e . s e t P r e f e r e n c e ( " b r o w s e r . d o w n l o a d . d i r " , " / t m p " )
p r o f i l e . s e t P r e f e r e n c e (
" b r o w s e r . h e l p e r A p p s . n e v e r A s k . s a v e T o D i s k " , " t e x t / c s v " )
d e f d r i v e r I n s t a n c e = n e w F i r e f o x D r i v e r ( p r o f i l e )
d r i v e r I n s t a n c e . m a n a g e ( ) . w i n d o w ( ) . m a x i m i z e ( )
d r i v e r I n s t a n c e
}
11 . 611 . 7
CHROME
Needs ChromeDriver downloaded
Pretty fast and stable
CHROME (1)
GebConfig.groovy
p r i v a t e S t r i n g d r i v e r L o c a t i o n D e p e n d i n g O n O p e r a t i n g S y s t e m ( ) {
S t r i n g o s = S y s t e m . g e t P r o p e r t y ( " o s . n a m e " ) . t o L o w e r C a s e ( ) ;
d e f l o c = " h t t p : / / c h r o m e d r i v e r . s t o r a g e . g o o g l e a p i s . c o m / 2 . 2 0 "
i f ( o s . c o n t a i n s ( ' m a c ' ) ) {
r e t u r n " $ { l o c } / c h r o m e d r i v e r _ m a c 3 2 . z i p "
}
i f ( o s . c o n t a i n s ( ' w i n ' ) ) {
r e t u r n " $ { l o c } / c h r o m e d r i v e r _ w i n 3 2 . z i p "
}
r e t u r n " $ { l o c } / c h r o m e d r i v e r _ l i n u x 6 4 . z i p "
}
11 . 8
CHROME (2)
GebConfig.groovy
p r i v a t e v o i d d o w n l o a d D r i v e r ( F i l e f i l e , S t r i n g p a t h ) {
i f ( ! f i l e . e x i s t s ( ) ) {
d e f a n t = n e w A n t B u i l d e r ( )
a n t . g e t ( s r c : p a t h , d e s t : ' d r i v e r . z i p ' )
a n t . u n z i p ( s r c : ' d r i v e r . z i p ' , d e s t : f i l e . p a r e n t )
a n t . d e l e t e ( f i l e : ' d r i v e r . z i p ' )
a n t . c h m o d ( f i l e : f i l e , p e r m : ' 7 0 0 ' )
}
}
11 . 9
CHROME (3)
GebConfig.groovy
d e f c h r o m e D r i v e r = n e w F i l e ( ' b u i l d / d r i v e r s / c h r o m e / c h r o m e d r i v e r ' )
d o w n l o a d D r i v e r ( c h r o m e D r i v e r ,
d r i v e r L o c a t i o n D e p e n d i n g O n O p e r a t i n g S y s t e m ( ) )
S y s t e m . s e t P r o p e r t y ( ' w e b d r i v e r . c h r o m e . d r i v e r ' ,
c h r o m e D r i v e r . a b s o l u t e P a t h )
d r i v e r = {
d e f d r i v e r I n s t a n c e = n e w C h r o m e D r i v e r ( )
d e f b r o w s e r W i n d o w = d r i v e r I n s t a n c e . m a n a g e ( ) . w i n d o w ( )
/ / w i d t h , h e i g h t
b r o w s e r W i n d o w . s i z e = n e w D i m e n s i o n ( 1 0 0 0 , 2 5 0 0 )
b r o w s e r W i n d o w . p o s i t i o n = n e w P o i n t ( 0 , 0 )
d r i v e r I n s t a n c e
}
11 . 10
WAITING
GebConfig.groovy
w a i t i n g {
t i m e o u t = 1 0
r e t r y I n t e r v a l = 0 . 5
}
b a s e N a v i g a t o r W a i t i n g = t r u e
a t C h e c k W a i t i n g = t r u e
11 . 11
USING WAITING
< d i v c l a s s = " f a d e - m e - i n " s t y l e = " d i s p l a y : n o n e " >
H i - a r e y o w a i t i n g f o r m e ?
< / d i v >
< s c r i p t >
$ ( ' d i v . f a d e - m e - i n ' ) . d e l a y ( 3 0 0 0 ) . s l i d e D o w n ( ) ;
< / s c r i p t >
s t a t i c c o n t e n t = {
f a d e I n M e s s a g e { $ ( ' d i v . f a d e - m e - i n ' ) }
}
t h e n :
w a i t F o r {
f a d e I n M e s s a g e . t e x t ( ) = = ' H i - a r e y o w a i t i n g f o r m e ? '
}
11 . 12
PAUSING GEB
p r i v a t e v o i d p a u s e ( ) {
j s . e x e c " " " ( f u n c t i o n ( ) {
w i n d o w . _ _ g e b P a u s e d = t r u e ;
v a r d i v = d o c u m e n t . c r e a t e E l e m e n t ( " d i v " ) ;
d i v . s e t A t t r i b u t e ( ' s t y l e ' , " p o s i t i o n : a b s o l u t e ; t o p : 0 p x ;  
z - i n d e x : 3 0 0 0 ; p a d d i n g : 1 0 p x ; b a c k g r o u n d - c o l o r : r e d ; ) ;
v a r b u t t o n = d o c u m e n t . c r e a t e E l e m e n t ( " b u t t o n " ) ;
b u t t o n . i n n e r H T M L = " U n p a u s e G e b " ;
b u t t o n . o n c l i c k = f u n c t i o n ( ) {
w i n d o w . _ _ g e b P a u s e d = f a l s e ;
}
d i v . a p p e n d C h i l d ( b u t t o n ) ;
d o c u m e n t . g e t E l e m e n t s B y T a g N a m e ( " b o d y " ) [ 0 ] . a p p e n d C h i l d ( d i v ) ;
} ) ( ) ; " " "
w a i t F o r ( 3 0 0 ) { ! j s . _ _ g e b P a u s e d }
}
11 . 1311 . 14
PAUSING GEB
w h e n :
p a u s e ( ) / / P a u s e G e b u n t i l b u t t o n p r e s s e d
12 . 1
REPORTING
12 . 2
TEST REPORTS
Nicely formatted
Spock power-assert format
SCREENSHOTS
Screenshots and HTML from end of each test:
Extend from GebReportingSpec
c l a s s A t t e n d e e F u n c t i o n a l S p e c e x t e n d s G e b R e p o r t i n g S p e c
GebConfig.groovy
r e p o r t s D i r = n e w F i l e ( " b u i l d / g e b - r e p o r t s " )
12 . 312 . 4
AD-HOC SCREENSHOTS
r e p o r t " W h e n - f o r m - i s - j u s t - f i l l e d "
Saves a report in reportsDir
Numbered in increasing order
13 . 1
JAVASCRIPT
In case you need to interact using javascript
13 . 2
EXECUTING JAVASCRIPT
Clicking a button that is hidden will create a
ElementNotVisibleException
< f i e l d s e t c l a s s = " w e l l " s t y l e = " d i s p l a y : n o n e " >
< g : l i n k c l a s s = " b t n " a c t i o n = " i n d e x " > L i s t < / g : l i n k >
< / f i e l d s e t >
13 . 3
EXECUTING JAVASCRIPT
J a v a s c r i p t E x e c u t o r e x e c u t o r = ( J a v a s c r i p t E x e c u t o r ) d r i v e r
e x e c u t o r . e x e c u t e S c r i p t ( ' j Q u e r y ( " . w e l l " ) . s h o w ( ) ; ' )
13 . 4
WRAPPING JAVASCRIPT
d e f j s ( S t r i n g s c r i p t ) {
( d r i v e r a s J a v a s c r i p t E x e c u t o r ) . e x e c u t e S c r i p t ( s c r i p t )
}
j s ( ' j Q u e r y ( " . w e l l " ) . s h o w ( ) ; ' )
13 . 5
JQUERY SHORTHAND
$ ( " d i v # a " ) . j q u e r y . m o u s e o v e r ( )
$ ( " # a " ) . j q u e r y . t r i g g e r ( ' m o u s e o v e r ' )
14
OTHER USAGES
Screenscraping of a site
Solving complex problems like 2048
RESOURCES
http://gebish.org
https://groups.google.com/forum/#!forum/geb-user
https://github.com/geb
https://gist.github.com/melix/9619800
https://github.com/tomaslin/grails-test-recipes
https://fbflex.wordpress.com/2010/08/25/geb-and-grails-
tips-tricks-and-gotchas/
https://github.com/JacobAae/gr8conf-in-2016-geb-for-
grails
1516
QUESTIONS?

Geb for Testing Your Grails Application GR8Conf India 2016

  • 1.
    1 GEB FOR TESTING YOURGRAILS APPLICATION Jacob Aae Mikkelsen
  • 2.
  • 3.
    AGENDA Functional testing Geb -cudos and history How Geb works Geb and Grails 2 and 3 Browser support Javascript
  • 4.
    3 . 1 JACOBAAE MIKKELSEN Senior Engineer at LEGO Microservice based architechture on JVM Previously 4 years at Gennemtænkt IT Consultant on Groovy and Grails External Associate Professor - University of Southern Denmark @JacobAae Blogs The Grails Diary
  • 5.
    3 . 24. 1 FUNCTIONAL TESTING
  • 7.
  • 8.
  • 9.
    4 . 4 FUNCTIONALTESTING Ignores the specifics of the underlying software component under test. Whitebox / Greybox Merely asserts that providing certain input results in certain output. Web-application: Programmatically controlling a web browser to simulate the actions of a user on a web page.
  • 10.
    4 . 5 BROWSERAUTOMATION
  • 11.
    4 . 64. 7 PAIN Traditionally been tedious, cumbersome and brittle to do  Geb helps ease the pain
  • 12.
    4 . 8 GAIN Anybrowser based application can be tested
  • 13.
  • 14.
    5 . 2 GEBHISTORY Started in November 2009 Created by Luke Daley Current project lead Marcin Erdman
  • 15.
    WHY GEB jQuery likeselector syntax Power of WebDriver (Easier api) Robustness of Page Object modeling Expressiveness of the Groovy language Integrates well with build systems (Gradle/Maven) Excellent user manual/documentation
  • 16.
    5 . 3 GEBIMPLEMENTATION Build on top of the WebDriver browser automation library successor to the Selenium Remote Control (RC) testing framework. Selenium RC → JavaScript to interact WebDriver → native browser drivers Use JUnit or Spock
  • 17.
    5 . 4 WEBDRIVER Veryactive development Stable API and feature set Verbose Low level Not a complete solution
  • 18.
  • 19.
  • 20.
    5 . 6 6. 2 NAVIGATOR The $() method returns a Navigator object General format $ ( < c s s s e l e c t o r > , < i n d e x / r a n g e > , < a t t r i b u t e / t e x t m a t c h e r s > )
  • 21.
    GEB SELECTORS (1) Jquerylike selecter syntax / / m a t c h a l l ' p ' e l e m e n t s o n p a g e $ ( " p " ) / / m a t c h t h e f i r s t ' p ' e l e m e n t o n t h e p a g e $ ( " p " , 0 ) / / A l l ' d i v ' e l e m e n t s w i t h a t i t l e v a l u e ' s e c t i o n ' $ ( " d i v " , t i t l e : " s e c t i o n " ) / / m a t c h t h e f i r s t ' d i v ' e l e m e n t t e x t ' s e c t i o n ' $ ( " d i v " , 0 , t e x t : " s e c t i o n " ) / / m a t c h t h e f i r s t ' d i v ' e l e m e n t w i t h t h e c l a s s ' m a i n ' $ ( " d i v . m a i n " , 0 )
  • 22.
    6 . 3 GEBSELECTORS (2) Text attribute supports regex / / A n y d i v w i t h t h e t e x t s t a r t i n g w i h G R 8 $ ( " d i v " , t e x t : ~ / G R 8 . + / ) $ ( " p " , t e x t : s t a r t s W i t h ( " G R 8 " ) ) / / A n d o t h e r h a n d y p r e d i c a t e s $ ( " d i v " , c l a s s : c o n t a i n s ( " u i - " ) )
  • 23.
    6 . 4 GEBSELECTORS (3) Selecting returns Navigatorobjects / / T h e p a r e n t o f t h e f i r s t d i v $ ( " d i v " , 0 ) . p a r e n t ( ) / / A l l t a b l e s w i t h a c e l l s p a c i n g / / a t t r i b u t e v a l u e o f 0 t h a t a r e n e s t e d i n a p a r a g r a p h $ ( " p " ) . f i n d ( " t a b l e " , c e l l s p a c i n g : ' 0 ' )
  • 24.
    6 . 56. 6 CSS SUPPORT $ ( " t a b l e t r : n t h - c h i l d ( 2 n + 1 ) t d " )
  • 25.
    6 . 7 RETRIVINGINFORMATION $ ( " p " ) . t e x t ( ) = = " a " $ ( " p " ) . t a g ( ) = = " p " $ ( " p " ) . @ t i t l e = = " a " $ ( " p " ) . c l a s s e s ( ) = = [ " a " , " p a r a " ]
  • 26.
    INTERACTION WITH CONTENT click() isDisplayed() withConfirm{} withAlert{} $( " a . b t n " ) . c l i c k ( ) $ ( " d i v " ) . i s D i s p l a y e d ( ) w i t h C o n f i r m { $ ( " b u t t o n . d e l e t e " ) . c l i c k ( ) }
  • 27.
    6 . 86. 9 SENDING INPUT i m p o r t o r g . o p e n q a . s e l e n i u m . K e y s / / S h o r t h a n d f o r s e n d K e y s ( ) m e t h o d o f W e b D r i v e r . $ ( " d i v " ) < < " a b c " $ ( " i n p u t " , n a m e : " f o o " ) < < K e y s . c h o r d ( K e y s . C O N T R O L , " c " )
  • 28.
    6 . 10 INTERACTION UsingActionsAPI from WebDriver is possible. But Geb provides the interactclosure
  • 29.
    6 . 11 CONTROL-CLICKING im p o r t o r g . o p e n q a . s e l e n i u m . K e y s i n t e r a c t { k e y D o w n K e y s . C T R L c l i c k $ ( " a . m y L i n k " ) k e y U p K e y s . C T R L }
  • 30.
    SIMULATE DRAG-N-DROP i nt e r a c t { c l i c k A n d H o l d ( $ ( ' # d r a g g a b l e ' ) ) m o v e B y O f f s e t ( 1 5 0 , 2 0 0 ) r e l e a s e ( ) } Or easier i n t e r a c t { d r a g A n d D r o p B y ( $ ( " # d r a g g a b l e " ) , 1 5 0 , 2 0 0 ) }
  • 31.
    6 . 12 MOREINTERACTION WITH FORMS Consider the following HTML… < f o r m > < i n p u t t y p e = " t e x t " n a m e = " g e b " v a l u e = " F u n c t i o n a l " / > < / f o r m > The value can be read and written via property notation… $ ( " f o r m " ) . g e b = = " F u n c t i o n a l " $ ( " f o r m " ) . g e b = " T e s t i n g " $ ( " f o r m " ) . g e b = = " T e s t i n g " These are literally shortcuts for… $ ( " f o r m " ) . f i n d ( " i n p u t " , n a m e : " g e b " ) . v a l u e ( ) = = " F u n c t i o n a l " $ ( " f o r m " ) . f i n d ( " i n p u t " , n a m e : " g e b " ) . v a l u e ( " T e s t i n g " ) $ ( " f o r m " ) . f i n d ( " i n p u t " , n a m e : " g e b " ) . v a l u e ( ) = = " T e s t i n g "
  • 32.
    6 . 13 VARIABLESAVAILABLE title browser currentUrl currentWindow
  • 33.
    6 . 14 MOREPOSSIBILITIES Uploading files Downloading files Interacting with javascript js object (Example later)
  • 34.
    6 . 157. 1 STANDALONE GEB SCRIPT
  • 35.
    GEB STANDALONE EXAMPLE Letstry to automate: Searching for GR8Conf India Click the first link Hopefully end up on the right homepage
  • 36.
    7 . 2 GEBSTANDALONE EXAMPLE g o " h t t p : / / d u c k d u c k g o . c o m " $ ( ' i n p u t ' , n a m e : ' q ' ) . v a l u e ( " G R 8 C o n f I n d i a " ) $ ( ' i n p u t ' , n a m e : ' q ' ) < < K e y s . E N T E R w a i t F o r ( 1 0 , 1 ) { $ ( " # l i n k s " ) . d i s p l a y e d } s l e e p ( 3 0 0 0 ) / / F o r d e m o r e a s o n s $ ( " h 2 . r e s u l t _ _ t i t l e " ) . f i r s t ( ) . c l i c k ( ) w a i t F o r { t i t l e = " G R 8 C o n f I N - 2 0 1 6 " }
  • 37.
    7 . 38. 1 STRUCTURING GEB TESTS
  • 38.
    SCENARIO Lets test aCRUD part of a grails application registrering conference attendees Lets test the following 1. Goto list of attendees page 2. Create new attendee (incl. invalid data once) 3. Update the attendee 4. Check data is updated
  • 39.
    8 . 28. 3 GEB SPEC BASICS i m p o r t g e b . s p o c k . G e b S p e c @ S t e p w i s e / / E n s u r e s t h e t e s t s a r e r u n s e q u e n t i a l l y c l a s s A t t e n d e e F u n c t i o n a l S p e c e x t e n d s G e b S p e c { / / S p o c k s p e c s h e r e }
  • 40.
    GEB SPEC (1) Thenaive inmaintainable way! v o i d " G o t o l i s t p a g e - c h e c k i n i t i a l s t a t e " ( ) { w h e n : " T h e h o m e p a g e i s v i s i t e d " g o ' / a t t e n d e e / i n d e x ' t h e n : t i t l e = = " A t t e n d e e L i s t " }
  • 41.
    8 . 4 GEBSPEC (2) The naive inmaintainable way! v o i d " C l i c k n e w a t t e n d e e b u t t o n " ( ) { w h e n : $ ( " a . c r e a t e " ) . c l i c k ( ) t h e n : t i t l e = = " C r e a t e A t t e n d e e " }
  • 42.
    8 . 5 GEBSPEC (3) The naive inmaintainable way! v o i d " S u b m i t f o r m w i t h e r r o r s " ( ) { w h e n : $ ( " b u t t o n . b t n - p r i m a r y " ) . c l i c k ( ) t h e n : t i t l e = = " C r e a t e A t t e n d e e " }
  • 43.
    8 . 6 GEBSPEC (4) The naive inmaintainable way! v o i d " S u b m i t f o r m w i t h n o e r r o r s " ( ) { w h e n : $ ( ' f o r m ' ) . n a m e = ' D e e p a k ' $ ( ' f o r m ' ) . e m a i l = ' d e e p a k @ m a i l . o r g ' a n d : $ ( " b u t t o n . b t n - p r i m a r y " ) . c l i c k ( ) t h e n : t i t l e = = ' S h o w A t t e n d e e ' $ ( ' d i v . p r o p e r t y - v a l u e ' ) . f i n d { i t . t e x t ( ) = = ' D e e p a k ' } $ ( ' d i v . p r o p e r t y - v a l u e ' ) . f i n d { i t . t e x t ( ) = = ' d e e p a k @ m a i l . o r g ' } }
  • 44.
    8 . 7 GEBSPEC (5) The naive inmaintainable way! v o i d " C l i c k E d i t B u t t o n " ( ) { w h e n : $ ( " a . b t n - p r i m a r y " ) . c l i c k ( ) t h e n : t i t l e = = ' E d i t A t t e n d e e ' }
  • 45.
    8 . 8 GEBSPEC (6) The naive inmaintainable way! v o i d " U p d a t e A t t e n d e e " ( ) { w h e n : $ ( ' f o r m ' ) . n a m e = ' A m i t ' $ ( ' f o r m ' ) . e m a i l = ' a m i t @ m a i l . o r g ' a n d : $ ( " b u t t o n . b t n - p r i m a r y " ) . c l i c k ( ) t h e n : t i t l e = = ' S h o w A t t e n d e e ' $ ( ' s p a n . p r o p e r t y - v a l u e ' ) . f i n d { i t . t e x t ( ) = = ' A m i t ' } $ ( ' s p a n . p r o p e r t y - v a l u e ' ) . f i n d { i t . t e x t ( ) = = ' a m i t @ m a i l . o r g ' } }
  • 46.
    8 . 99. 1 GEB SPEC - THE BETTER WAY If we make a few scenarios, there will be Much duplication Many places to correct if we change the layout / DOM
  • 48.
    9 . 29. 3 SOLUTION Use pages and modules
  • 49.
    PAGE OBJECTS Describes aweb page Url How to check if we are at the correct place Content we wish to interact with .. and how it is found Helper methods
  • 50.
    9 . 4 PAGEOBJECTS i m p o r t e u . g r 8 c o n f . g r a i l s d e m o . m o d u l e s . N a v i g a t i o n B a r M o d u l e i m p o r t g e b . P a g e c l a s s A t t e n d e e S h o w P a g e e x t e n d s P a g e { s t a t i c u r l = " / a t t e n d e e / s h o w " s t a t i c a t = { t i t l e = = ~ / S h o w A t t e n d e e / } s t a t i c c o n t e n t = { a t t P r o p { $ ( ' s p a n . p r o p e r t y - l a b e l ' ) } n a m e { a t t P r o p . f i n d { i t . t e x t ( ) = = ' N a m e ' } . n e x t ( ) . t e x t ( ) } e m a i l { a t t P r o . f i n d { i t . t e x t ( ) = = ' E m a i l ' } . n e x t ( ) . t e x t ( ) } e d i t B u t t o n { $ ( " a . b t n - p r i m a r y " ) } } }
  • 51.
    9 . 59. 6 CONTENT CLOSURE s t a t i c c o n t e n t = { i n f o ( r e q u i r e d : f a l s e ) { $ ( " d i v . i n f o " ) } m e s s a g e ( w a i t : f a l s e ) { $ ( " d i v . m e s s a g e " ) } }
  • 52.
    9 . 7 MODULES Describesrepeated content Across pages Within the same page
  • 53.
    MODULES i m po r t g e b . M o d u l e c l a s s N a v i g a t i o n B a r M o d u l e e x t e n d s M o d u l e { s t a t i c b a s e = { $ ( ' n a v . n a v b a r ' ) } s t a t i c c o n t e n t = { h o m e ( r e q u i r e d : f a l s e ) { $ ( ' a . h o m e ' ) } l i s t A t t e n d e e ( r e q u i r e d : f a l s e ) { $ ( ' a . l i s t ' ) } n e w A t t e n d e e ( r e q u i r e d : f a l s e ) { $ ( ' a . c r e a t e ' ) } } }
  • 54.
    9 . 89. 9 MODULES s t a t i c c o n t e n t = { / / L i k e t h i s , t h e m o d u l e d o e s n o t n e e d a b a s e / / f o r m { m o d u l e N a v i g a t i o n B a r M o d u l e , $ ( ' n a v . n a v b a r ' ) } f o r m { m o d u l e N a v i g a t i o n B a r M o d u l e } }
  • 55.
    MODULE FOR REPEATEDCONTENT IN A PAGE i m p o r t g e b . M o d u l e c l a s s A t t e n d e e L i s t I t e m M o d u l e e x t e n d s M o d u l e { s t a t i c c o n t e n t = { d a t a { $ ( " t d " , i t ) } n a m e { d a t a ( 0 ) . t e x t ( ) } e m a i l { d a t a ( 1 ) . t e x t ( ) } n a t i o n a l i t y { d a t a ( 2 ) . t e x t ( ) } d a t e C r e a t e d { d a t a ( 3 ) . t e x t ( ) } l a s t U p d a t e d { d a t a ( 4 ) . t e x t ( ) } } }
  • 56.
    9 . 10 MODULEFOR REPEATED CONTENT IN A PAGE AttendeeListPage.groovy s t a t i c c o n t e n t = { m e n u b a r { m o d u l e N a v i g a t i o n B a r M o d u l e } a t t e n d e e s { m o d u l e L i s t A t t e n d e e L i s t I t e m M o d u l e , $ ( " t a b l e t r " ) . t a i l ( ) } }
  • 57.
    9 . 119. 12 MODULE FOR REPEATED CONTENT IN A PAGE w h e n : t o A t t e n d e e L i s t P a g e t h e n : a t t e n d e e s * . n a m e . c o n t a i n s ( ' B u r t B e c k w i t h ' )
  • 58.
    GEB SPEC -STRUCTURED (1) Lets try to restructure the ugly spec from before v o i d " G o t o l i s t p a g e - c h e c k i n i t i a l s t a t e " ( ) { w h e n : t o A t t e n d e e I n d e x P a g e t h e n : a t A t t e n d e e I n d e x P a g e }
  • 59.
    9 . 139. 14 GEB SPEC - STRUCTURED (2) v o i d " C l i c k n e w a t t e n d e e b u t t o n " ( ) { w h e n : m e n u b a r . n e w A t t e n d e e . c l i c k ( ) t h e n : a t A t t e n d e e C r e a t e P a g e }
  • 60.
    9 . 15 GEBSPEC - STRUCTURED (3) v o i d " S u b m i t f o r m w i t h e r r o r s " ( ) { w h e n : s u b m i t B u t t o n . c l i c k ( ) t h e n : a t A t t e n d e e C r e a t e P a g e }
  • 61.
    GEB SPEC -STRUCTURED (4) v o i d " S u b m i t f o r m w i t h n o e r r o r s " ( ) { w h e n : f o r m . n a m e = ' D e e p a k ' f o r m . e m a i l = ' d e e p a k @ m a i l . o r g ' a n d : s u b m i t B u t t o n . c l i c k ( ) t h e n : a t A t t e n d e e S h o w P a g e n a m e = = ' D e e p a k ' e m a i l = = ' d e e p a k @ m a i l . o r g ' }
  • 62.
    9 . 169. 17 GEB SPEC - STRUCTURED (5) v o i d " C l i c k E d i t B u t t o n " ( ) { w h e n : e d i t B u t t o n . c l i c k ( ) t h e n : a t A t t e n d e e E d i t P a g e }
  • 63.
    GEB SPEC -STRUCTURED (6) v o i d " U p d a t e A t t e n d e e " ( ) { w h e n : f o r m . n a m e = ' A m i t ' f o r m . e m a i l = ' a m i t @ s o m e m a i l . c o m ' a n d : u p d a t e B u t t o n . c l i c k ( ) t h e n : a t A t t e n d e e S h o w P a g e n a m e = = ' A m i t ' e m a i l = = ' a m i t @ s o m e m a i l . c o m ' }
  • 64.
    9 . 18 STANDALONEREVISITED t o D u c k D u c k G o P a g e i n p u t F i e l d < < " G R 8 C o n f I n d i a " s u b m i t ( ) w a i t F o r ( 1 0 , 0 . 5 ) { a t D u c k D u c k G o R e s u l t P a g e } s l e e p ( 3 0 0 0 ) / / F o r d e m o r e a s o n s c l i c k L i n k ( 0 ) w a i t F o r { a t G R 8 C o n f I n d i a P a g e }
  • 65.
    9 . 19 STANDALONEREVISITED c l a s s D u c k D u c k G o P a g e e x t e n d s g e b . P a g e { s t a t i c u r l = " h t t p : / / d u c k d u c k g o . c o m " s t a t i c a t = { t i t l e = = ~ / D u c k D u c k G o / } s t a t i c c o n t e n t = { i n p u t F i e l d { $ ( ' i n p u t ' , n a m e : ' q ' ) } } d e f s u b m i t ( ) { i n p u t F i e l d < < K e y s . E N T E R } }
  • 66.
    9 . 2010. 1 GEB WITH GRAILS
  • 67.
    GEB AND GRAILS2.X Must install plugin in BuildConfig.groovy d e p e n d e n c i e s { . . . t e s t ( " o r g . s e l e n i u m h q . s e l e n i u m : s e l e n i u m - s u p p o r t : 2 . 4 5 . 0 " ) t e s t ( " o r g . s e l e n i u m h q . s e l e n i u m : s e l e n i u m - f i r e f o x - d r i v e r : 2 . 4 5 . 0 " ) t e s t " o r g . g e b i s h : g e b - s p o c k : 0 . 1 0 . 0 " } p l u g i n s { . . . t e s t " o r g . g r a i l s . p l u g i n s : g e b : 0 . 1 0 . 0 " }
  • 68.
    10 . 210. 3 GEB TESTS IN GRAILS 2.X Tests placed in test/functionalfolder Running the tests g r a i l s t e s t - a p p f u n c t i o n a l :
  • 69.
    GEB AND GRAILS3 Geb is default in build.gradle d e p e n d e n c i e s { . . . t e s t C o m p i l e " o r g . g r a i l s . p l u g i n s : g e b " / / N o t e : I t i s r e c o m m e n d e d t o u p d a t e t o a m o r e r o b u s t d r i v e r / / ( C h r o m e , F i r e f o x e t c . ) t e s t R u n t i m e ' o r g . s e l e n i u m h q . s e l e n i u m : s e l e n i u m - h t m l u n i t - d r i v e r : 2 . 4 4 . 0 }
  • 70.
    10 . 4 GEBTESTS IN GRAILS 3 Creating Geb Spec g r a i l s c r e a t e - f u n c t i o n a l - t e s t M y G e b S c e n a r i o Placing the test in src/integration-test/groovy Running the tests g r a i l s t e s t - a p p - i n t e g r a t i o n
  • 71.
    10 . 5 GENERATEDCLASS @ I n t e g r a t i o n @ R o l l b a c k c l a s s M a n y A t t e n d e e s S p e c e x t e n d s G e b S p e c { v o i d " t e s t s o m e t h i n g " ( ) { w h e n : " T h e h o m e p a g e i s v i s i t e d " g o ' / ' t h e n : " T h e t i t l e i s c o r r e c t " $ ( ' t i t l e ' ) . t e x t ( ) = = " W e l c o m e t o G r a i l s " } }
  • 72.
    10 . 610. 7 INTERACTING WITH APPLICATION When some functionality is needed that is not exposed through the browser, it can be necessary to interact with the application under test.
  • 73.
    GRAILS 2.5 Tests notrunning in same JVM Done with remote-control plugin Send a closure for execution in application c o m p i l e " : r e m o t e - c o n t r o l : 2 . 0 "
  • 74.
    10 . 810. 9 REMOTE CONTROL s e t u p : ' C r e a t e s o m e i t e m n o t a v a i l a b l e t h r o u g h G U I ' d e f i d = r e m o t e { I t e m i t e m = n e w I t e m ( n a m e : " M y I t e m " ) i t e m . s a v e ( ) i t e m . i d }
  • 75.
    10 . 10 GRAILS3.0 Application is in same jvm Interaction is possible directly Tests run as integration tests
  • 76.
    INTERACTING WITH APPLICATION vo i d " T e s t P a g i n a t i o n i s s h o w n w i t h 1 5 a t t e n d e e s " ( ) { s e t u p : A t t e n d e e . w i t h N e w T r a n s a c t i o n { 1 5 . t i m e s { n e w A t t e n d e e ( n a m e : " N $ i t " , e m a i l : " m $ i t @ t . d k " ) . s a v e ( ) } } w h e n : t o A t t e n d e e I n d e x P a g e t h e n : h a s P a g i n a t i o n ( ) }
  • 77.
    10 . 1110. 12 INTERACTING WITH APPLICATION s t a t i c c o n t e n t = { m e n u b a r { m o d u l e N a v i g a t i o n B a r M o d u l e } p a g i n a t i o n ( r e q u i r e d : f a l s e ) { $ ( ' s p a n . c u r r e n t S t e p ' ) } } b o o l e a n h a s P a g i n a t i o n ( ) { p a g i n a t i o n . t e x t ( ) }
  • 78.
    11 . 1 CONFIGURATIONAND BROWSER SUPPORT
  • 79.
    11 . 2 GEBCONFIG Configurationfor Geb is placed in GebConfig.groovy In Grails 3, place it in ´src/integration-test/groovy`  http://www.gebish.org/manual/current/configuration.htm
  • 80.
    11 . 3 DRIVER Itis possible to configure the browser used.
  • 81.
  • 82.
    11 . 411. 5 DRIVER It is possible to configure the browser used. build.gradle c o m p i l e ' o r g . s e l e n i u m h q . s e l e n i u m : s e l e n i u m - c h r o m e - d r i v e r : 2 . 4 9 . 0 ' c o m p i l e ' o r g . s e l e n i u m h q . s e l e n i u m : s e l e n i u m - f i r e f o x - d r i v e r : 2 . 4 9 . 0 '
  • 83.
    FIREFOX GebConfig.groovy i m po r t o r g . o p e n q a . s e l e n i u m . f i r e f o x . F i r e f o x P r o f i l e i m p o r t o r g . o p e n q a . s e l e n i u m . f i r e f o x . F i r e f o x D r i v e r d r i v e r = { F i r e f o x P r o f i l e p r o f i l e = n e w F i r e f o x P r o f i l e ( ) p r o f i l e . s e t P r e f e r e n c e ( " b r o w s e r . d o w n l o a d . f o l d e r L i s t " , 2 ) p r o f i l e . s e t P r e f e r e n c e ( " b r o w s e r . d o w n l o a d . d i r " , " / t m p " ) p r o f i l e . s e t P r e f e r e n c e ( " b r o w s e r . h e l p e r A p p s . n e v e r A s k . s a v e T o D i s k " , " t e x t / c s v " ) d e f d r i v e r I n s t a n c e = n e w F i r e f o x D r i v e r ( p r o f i l e ) d r i v e r I n s t a n c e . m a n a g e ( ) . w i n d o w ( ) . m a x i m i z e ( ) d r i v e r I n s t a n c e }
  • 84.
    11 . 611. 7 CHROME Needs ChromeDriver downloaded Pretty fast and stable
  • 85.
    CHROME (1) GebConfig.groovy p ri v a t e S t r i n g d r i v e r L o c a t i o n D e p e n d i n g O n O p e r a t i n g S y s t e m ( ) { S t r i n g o s = S y s t e m . g e t P r o p e r t y ( " o s . n a m e " ) . t o L o w e r C a s e ( ) ; d e f l o c = " h t t p : / / c h r o m e d r i v e r . s t o r a g e . g o o g l e a p i s . c o m / 2 . 2 0 " i f ( o s . c o n t a i n s ( ' m a c ' ) ) { r e t u r n " $ { l o c } / c h r o m e d r i v e r _ m a c 3 2 . z i p " } i f ( o s . c o n t a i n s ( ' w i n ' ) ) { r e t u r n " $ { l o c } / c h r o m e d r i v e r _ w i n 3 2 . z i p " } r e t u r n " $ { l o c } / c h r o m e d r i v e r _ l i n u x 6 4 . z i p " }
  • 86.
    11 . 8 CHROME(2) GebConfig.groovy p r i v a t e v o i d d o w n l o a d D r i v e r ( F i l e f i l e , S t r i n g p a t h ) { i f ( ! f i l e . e x i s t s ( ) ) { d e f a n t = n e w A n t B u i l d e r ( ) a n t . g e t ( s r c : p a t h , d e s t : ' d r i v e r . z i p ' ) a n t . u n z i p ( s r c : ' d r i v e r . z i p ' , d e s t : f i l e . p a r e n t ) a n t . d e l e t e ( f i l e : ' d r i v e r . z i p ' ) a n t . c h m o d ( f i l e : f i l e , p e r m : ' 7 0 0 ' ) } }
  • 87.
    11 . 9 CHROME(3) GebConfig.groovy d e f c h r o m e D r i v e r = n e w F i l e ( ' b u i l d / d r i v e r s / c h r o m e / c h r o m e d r i v e r ' ) d o w n l o a d D r i v e r ( c h r o m e D r i v e r , d r i v e r L o c a t i o n D e p e n d i n g O n O p e r a t i n g S y s t e m ( ) ) S y s t e m . s e t P r o p e r t y ( ' w e b d r i v e r . c h r o m e . d r i v e r ' , c h r o m e D r i v e r . a b s o l u t e P a t h ) d r i v e r = { d e f d r i v e r I n s t a n c e = n e w C h r o m e D r i v e r ( ) d e f b r o w s e r W i n d o w = d r i v e r I n s t a n c e . m a n a g e ( ) . w i n d o w ( ) / / w i d t h , h e i g h t b r o w s e r W i n d o w . s i z e = n e w D i m e n s i o n ( 1 0 0 0 , 2 5 0 0 ) b r o w s e r W i n d o w . p o s i t i o n = n e w P o i n t ( 0 , 0 ) d r i v e r I n s t a n c e }
  • 88.
    11 . 10 WAITING GebConfig.groovy wa i t i n g { t i m e o u t = 1 0 r e t r y I n t e r v a l = 0 . 5 } b a s e N a v i g a t o r W a i t i n g = t r u e a t C h e c k W a i t i n g = t r u e
  • 89.
    11 . 11 USINGWAITING < d i v c l a s s = " f a d e - m e - i n " s t y l e = " d i s p l a y : n o n e " > H i - a r e y o w a i t i n g f o r m e ? < / d i v > < s c r i p t > $ ( ' d i v . f a d e - m e - i n ' ) . d e l a y ( 3 0 0 0 ) . s l i d e D o w n ( ) ; < / s c r i p t > s t a t i c c o n t e n t = { f a d e I n M e s s a g e { $ ( ' d i v . f a d e - m e - i n ' ) } } t h e n : w a i t F o r { f a d e I n M e s s a g e . t e x t ( ) = = ' H i - a r e y o w a i t i n g f o r m e ? ' }
  • 90.
    11 . 12 PAUSINGGEB p r i v a t e v o i d p a u s e ( ) { j s . e x e c " " " ( f u n c t i o n ( ) { w i n d o w . _ _ g e b P a u s e d = t r u e ; v a r d i v = d o c u m e n t . c r e a t e E l e m e n t ( " d i v " ) ; d i v . s e t A t t r i b u t e ( ' s t y l e ' , " p o s i t i o n : a b s o l u t e ; t o p : 0 p x ; z - i n d e x : 3 0 0 0 ; p a d d i n g : 1 0 p x ; b a c k g r o u n d - c o l o r : r e d ; ) ; v a r b u t t o n = d o c u m e n t . c r e a t e E l e m e n t ( " b u t t o n " ) ; b u t t o n . i n n e r H T M L = " U n p a u s e G e b " ; b u t t o n . o n c l i c k = f u n c t i o n ( ) { w i n d o w . _ _ g e b P a u s e d = f a l s e ; } d i v . a p p e n d C h i l d ( b u t t o n ) ; d o c u m e n t . g e t E l e m e n t s B y T a g N a m e ( " b o d y " ) [ 0 ] . a p p e n d C h i l d ( d i v ) ; } ) ( ) ; " " " w a i t F o r ( 3 0 0 ) { ! j s . _ _ g e b P a u s e d } }
  • 91.
    11 . 1311. 14 PAUSING GEB w h e n : p a u s e ( ) / / P a u s e G e b u n t i l b u t t o n p r e s s e d
  • 92.
  • 93.
    12 . 2 TESTREPORTS Nicely formatted Spock power-assert format
  • 94.
    SCREENSHOTS Screenshots and HTMLfrom end of each test: Extend from GebReportingSpec c l a s s A t t e n d e e F u n c t i o n a l S p e c e x t e n d s G e b R e p o r t i n g S p e c GebConfig.groovy r e p o r t s D i r = n e w F i l e ( " b u i l d / g e b - r e p o r t s " )
  • 95.
    12 . 312. 4 AD-HOC SCREENSHOTS r e p o r t " W h e n - f o r m - i s - j u s t - f i l l e d " Saves a report in reportsDir Numbered in increasing order
  • 96.
    13 . 1 JAVASCRIPT Incase you need to interact using javascript
  • 97.
    13 . 2 EXECUTINGJAVASCRIPT Clicking a button that is hidden will create a ElementNotVisibleException < f i e l d s e t c l a s s = " w e l l " s t y l e = " d i s p l a y : n o n e " > < g : l i n k c l a s s = " b t n " a c t i o n = " i n d e x " > L i s t < / g : l i n k > < / f i e l d s e t >
  • 98.
    13 . 3 EXECUTINGJAVASCRIPT J a v a s c r i p t E x e c u t o r e x e c u t o r = ( J a v a s c r i p t E x e c u t o r ) d r i v e r e x e c u t o r . e x e c u t e S c r i p t ( ' j Q u e r y ( " . w e l l " ) . s h o w ( ) ; ' )
  • 99.
    13 . 4 WRAPPINGJAVASCRIPT d e f j s ( S t r i n g s c r i p t ) { ( d r i v e r a s J a v a s c r i p t E x e c u t o r ) . e x e c u t e S c r i p t ( s c r i p t ) } j s ( ' j Q u e r y ( " . w e l l " ) . s h o w ( ) ; ' )
  • 100.
    13 . 5 JQUERYSHORTHAND $ ( " d i v # a " ) . j q u e r y . m o u s e o v e r ( ) $ ( " # a " ) . j q u e r y . t r i g g e r ( ' m o u s e o v e r ' )
  • 101.
    14 OTHER USAGES Screenscraping ofa site Solving complex problems like 2048
  • 102.
  • 103.