Geb for Testing Your Grails Application GR8Conf India 2016

Post on 21-Apr-2017

641 views 0 download

Transcript of Geb for Testing Your Grails Application GR8Conf India 2016

1

GEB FOR TESTINGYOUR GRAILSAPPLICATION

Jacob Aae Mikkelsen

2

SURVEY AND RULES

AGENDAFunctional testing

Geb - cudos and history

How Geb works

Geb and Grails 2 and 3

Browser support

Javascript

3 . 1

JACOB AAE MIKKELSENSenior 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 SouthernDenmark

@JacobAae

Blogs The Grails Diary

3 . 24 . 1

FUNCTIONAL TESTING

4 . 2

WHY?

4 . 3

4 . 4

FUNCTIONAL TESTINGIgnores the specifics of the underlying softwarecomponent under test.

Whitebox / Greybox

Merely asserts that providing certain input results incertain output.

Web-application: Programmatically controlling a webbrowser to simulate the actions of a user on a web page.

4 . 5

BROWSER AUTOMATION

4 . 64 . 7

PAINTraditionally been tedious, cumbersome and brittleto do

Geb helps ease the pain

4 . 8

GAINAny browser based application can be tested

5 . 1

GEB

5 . 2

GEB HISTORYStarted in November 2009

Created by Luke Daley

Current project lead Marcin Erdman

WHY GEBjQuery 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 IMPLEMENTATIONBuild on top of the WebDriver browser automation library

successor to the Selenium Remote Control (RC) testingframework.

Selenium RC → JavaScript to interact

WebDriver → native browser drivers

Use JUnit or Spock

5 . 4

WEBDRIVERVery active development

Stable API and feature set

Verbose

Low level

Not a complete solution

5 . 5

5 . 66 . 1

USING GEB

5 . 6

6 . 2

NAVIGATORThe $() 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 Navigator objects

/ / 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 CONTENTclick()

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 INPUTi 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

INTERACTIONUsing Actions API from WebDriver is possible.

But Geb provides the interact closure

6 . 11

CONTROL-CLICKINGi 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-DROPi 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 FORMSConsider 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 AVAILABLEtitle

browser

currentUrl

currentWindow

6 . 14

MORE POSSIBILITIESUploading files

Downloading files

Interacting with javascript

js object (Example later)

6 . 157 . 1

STANDALONE GEB SCRIPT

GEB STANDALONE EXAMPLELets try to automate:

Searching for GR8Conf India

Click the first link

Hopefully end up on the right homepage

7 . 2

GEB STANDALONE EXAMPLEg 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

SCENARIOLets 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 BASICSi 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 WAYIf we make a few scenarios, there will be

Much duplication

Many places to correct if we change the layout / DOM

9 . 29 . 3

SOLUTIONUse pages and modules

PAGE OBJECTSDescribes 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 OBJECTSi 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 CLOSUREs 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

MODULESDescribes repeated content

Across pages

Within the same page

MODULESi 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

MODULESs 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 CONTENTIN 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 CONTENTIN 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 ( ) } }

MODULE FOR REPEATED CONTENT

9 . 119 . 12

MODULE FOR REPEATED CONTENTIN 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 REVISITEDc 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.XMust 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.XTests placed in test/functional folder

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 3Geb 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 3Creating 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 APPLICATIONWhen some functionality is needed that is not exposed

through the browser, it can be necessary to interact with theapplication under test.

GRAILS 2.5Tests 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 CONTROLs 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.0Application is in same jvm

Interaction is possible directly

Tests run as integration tests

INTERACTING WITH APPLICATIONv 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 ( ) }

INTERACTING WITH APPLICATION

10 . 1110 . 12

INTERACTING WITH APPLICATIONs 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 BROWSERSUPPORT

11 . 2

GEBCONFIGConfiguration for Geb is placed in GebConfig.groovy

In Grails 3, place it in ´src/integration-test/groovy`

http://www.gebish.org/manual/current/configuration.html

11 . 3

DRIVERIt is possible to configure the browser used.

SUPPORTED DRIVERSFirefox

Chrome

InternetExplorer

Safari

HtmlUnit

PhantomJS

11 . 411 . 5

DRIVERIt 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 '

FIREFOXGebConfig.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

CHROMENeeds 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

WAITINGGebConfig.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 GEBp 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 GEBw 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 REPORTSNicely formatted

Spock power-assert format

SCREENSHOTSScreenshots 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 SCREENSHOTSr 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

JAVASCRIPTIn case you need to interact using javascript

13 . 2

EXECUTING JAVASCRIPTClicking a button that is hidden will create aElementNotVisibleException

< 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 JAVASCRIPTJ 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 JAVASCRIPTd 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 USAGESScreenscraping of a site

Solving complex problems like 2048

RESOURCEShttp://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?