Your SlideShare is downloading. ×
0
Groovier Selenium
                                Denver JUG 8/13/2008
                                    Frederic Jean
 ...
who am i


Wednesday, August 13, 2008              2
Topics
                             (Really) Quick intro to Selenium
                             Groovy Metaprogramming t...
Selenium

                             UI testing tool
                             Runs in browser
                      ...
Selenium IDE

                             Firefox plugin
                             Simplifies writing and testing Selen...
Selenese
                             HTML Tables
                              Action
                              Targe...
Example
                                         BlogTest

                             open                   /

        ...
Selenese Locators

                             Allows an action to target a specific
                             DOM elem...
Selenese Locators
                             Locator Type               Description
                                    ...
Sample Test


Wednesday, August 13, 2008                 10
Selenese TestSuites

                             Groups and organizes individual
                             Selenese te...
Selenium RC

                             Runs as a process on a system
                             Listens to requests o...
Selenium RC Drivers
                             Drives the Selenium RC Ser ver
                             programatical...
Generating Java Test


Wednesday, August 13, 2008             14
Generated Java
    package com.example.tests;

    import com.thoughtworks.selenium.*;
    import java.util.regex.Pattern;...
Generated Java
                                  Where
    package com.example.tests;

    import com.thoughtworks.seleniu...
Generated Java
    package com.example.tests;

    import com.thoughtworks.selenium.*;  Need to rename class.
    import j...
Generated Java from
                                     Must inherit
                                                    ...
Generated Java
    package com.example.tests;

    import com.thoughtworks.selenium.*;
    import java.util.regex.Pattern;...
Generated Java
    package com.example.tests;


                                    selenium.this
    import com.thoughtwo...
Generated Java
    package com.example.tests;

    import com.thoughtworks.selenium.*;
    import java.util.regex.Pattern;...
Generated Java

                             Good start
                             Needs some work to be useful
        ...
Groovy
           package com.example.tests

           import com.thoughtworks.selenium.*

           class NewGroovyTest...
Groovy

                             Less noisy than Java
                             Still repetitive
                  ...
Metaprogramming


                      Writing of computer programs that write or
                      manipulate other ...
Metaprogramming

                             Increases code expressiveness
                             Allows SMEs to un...
Meta Object Protocol
                             Establishes the rules behind method
                             calling...
Metaclass

                             All Groovy objects have one
                             Can be defined for Java ob...
Delegation
                             For ward method calls to another object
                             Tedious to do...
Groovy Delegation
                             /**
                               * Called when a method cannot be found i...
Goodbye Repetition
                             void testDelegation() {
                                 open quot;/quot;
...
Performance Hit




Wednesday, August 13, 2008                     32
Performance Hit




Wednesday, August 13, 2008                     33
Intercept, Cache, Invoke

       /**
         * Called when a method cannot be found in the class
         * or the meta c...
Performance Hit




Wednesday, August 13, 2008                     35
Groovy Delegation

                             Results in cleaner test code
                             Almost trivial t...
waitForTextPresent?
                             void testDelegation() {
                                 open quot;/quot;...
waitForTextPresent?
                             void testDelegation() {
                                 open quot;/quot;...
waitForTextPresent?
                             void testDelegation() {
                                 open quot;/quot;...
waitForTextPresent?

                             Selenese generates waitFor, verify, and
                             ass...
Wait a minute...

                             JavaScript is a dynamic language...
                             Groovy is ...
Synthetic Methods

                             Methods that don't really exist
                             Grails finder ...
Steps to Take

                             Identify synthetic methods
                             Implement behavior
   ...
Identifying Methods
                             def methodMissing(String name, args) {
                                 s...
Implement Behavior
                private waitFor(name, args) {
                    // Make the bold assumption that the ...
Locating Getter

                def getMethodName(prefix, name) {
                    [quot;isquot;, quot;getquot;].colle...
Loop Begone!

                             void testDelegation() {
                                 open quot;/quot;
     ...
Refactor!
                             Move methods to super class
                              methodMissing
           ...
GroovierSelenium
                             Extends SeleneseTestCase with
                             methodMissing
   ...
Near Future

                             JUnit 4.5 test runners
                              GroovierSeleniumRunner
    ...
NetBeans & Groovy

                             Grails and Groovy Plugin integrated
                             with NetB...
Looking Back
                             Talked about Selenium
                             Leveraging Groovy metaprogram...
Book


                             Programming Groovy (Venkat S.)




Wednesday, August 13, 2008                         ...
Links

                             http://groovy.codehaus.org
                             http://groovierselenium.google...
Upcoming SlideShare
Loading in...5
×

Groovier Selenium (Djug)

13,305

Published on

Presentation on using Groovy's metaprogramming capabilities to simplify writing Selenium tests.

Published in: Technology
1 Comment
5 Likes
Statistics
Notes
No Downloads
Views
Total Views
13,305
On Slideshare
0
From Embeds
0
Number of Embeds
3
Actions
Shares
0
Downloads
130
Comments
1
Likes
5
Embeds 0
No embeds

No notes for slide

Transcript of "Groovier Selenium (Djug)"

  1. 1. Groovier Selenium Denver JUG 8/13/2008 Frederic Jean fred@fredjean.net frederic.jean@sun.com Wednesday, August 13, 2008 1
  2. 2. who am i Wednesday, August 13, 2008 2
  3. 3. Topics (Really) Quick intro to Selenium Groovy Metaprogramming through progressive refactorings of a single, simple yet verbose Java Selenium RC Driver example. A few sundry items Wednesday, August 13, 2008 3
  4. 4. Selenium UI testing tool Runs in browser Well suited for Ajax applications Wednesday, August 13, 2008 4
  5. 5. Selenium IDE Firefox plugin Simplifies writing and testing Selenese test case Can record and play back Selenese tests Wednesday, August 13, 2008 5
  6. 6. Selenese HTML Tables Action Target Value Intepreted by Selenium Core Actions match JavaScript functions Wednesday, August 13, 2008 6
  7. 7. Example BlogTest open / clickAndWait link=Adoption Out of my mind... : category assertTitle adoption Wednesday, August 13, 2008 7
  8. 8. Selenese Locators Allows an action to target a specific DOM element on the page <type>=<locator> Wednesday, August 13, 2008 8
  9. 9. Selenese Locators Locator Type Description The name of an input element on a name form The id associated with an element id on a page The text contained within an anchor link element (<a/>) A JavaScript expression that dom returns an element An XPath expression pointing to an xpath element on the page Wednesday, August 13, 2008 9
  10. 10. Sample Test Wednesday, August 13, 2008 10
  11. 11. Selenese TestSuites Groups and organizes individual Selenese tests Can be run through ant Wednesday, August 13, 2008 11
  12. 12. Selenium RC Runs as a process on a system Listens to requests on a specific port Has drivers for different languages Wednesday, August 13, 2008 12
  13. 13. Selenium RC Drivers Drives the Selenium RC Ser ver programatically Allows integration with xUnit frameworks Flow control and conditionals Java driver provides a SeleneseTestCase class Wednesday, August 13, 2008 13
  14. 14. Generating Java Test Wednesday, August 13, 2008 14
  15. 15. Generated Java package com.example.tests; import com.thoughtworks.selenium.*; import java.util.regex.Pattern; public class NewTest extends SeleneseTestCase { public void setUp() throws Exception { setUp(quot;http://fredjean.net/quot;, quot;*chromequot;); } public void testNew() throws Exception { selenium.open(quot;/quot;); selenium.click(quot;link=Adoptionquot;); selenium.waitForPageToLoad(quot;30000quot;); assertEquals(quot;Out of my mind... : category adoptionquot;, selenium.getTitle()); selenium.click(quot;link=We Are Out!quot;); for (int second = 0;; second++) { if (second >= 60) fail(quot;timeoutquot;); try { if (selenium.isTextPresent(quot;Commentsquot;)) break; } catch (Exception e) {} Thread.sleep(1000); } assertTrue(selenium.isTextPresent(quot;Trackbacksquot;)); } } Wednesday, August 13, 2008 15
  16. 16. Generated Java Where package com.example.tests; import com.thoughtworks.selenium.*; is it used? import java.util.regex.Pattern; public class NewTest extends SeleneseTestCase { public void setUp() throws Exception { setUp(quot;http://fredjean.net/quot;, quot;*chromequot;); } public void testNew() throws Exception { selenium.open(quot;/quot;); selenium.click(quot;link=Adoptionquot;); selenium.waitForPageToLoad(quot;30000quot;); assertEquals(quot;Out of my mind... : category adoptionquot;, selenium.getTitle()); selenium.click(quot;link=We Are Out!quot;); for (int second = 0;; second++) { if (second >= 60) fail(quot;timeoutquot;); try { if (selenium.isTextPresent(quot;Commentsquot;)) break; } catch (Exception e) {} Thread.sleep(1000); } assertTrue(selenium.isTextPresent(quot;Trackbacksquot;)); } } Wednesday, August 13, 2008 16
  17. 17. Generated Java package com.example.tests; import com.thoughtworks.selenium.*; Need to rename class. import java.util.regex.Pattern; public class NewTest extends SeleneseTestCase { public void setUp() throws Exception { setUp(quot;http://fredjean.net/quot;, quot;*chromequot;); } public void testNew() throws Exception { selenium.open(quot;/quot;); selenium.click(quot;link=Adoptionquot;); selenium.waitForPageToLoad(quot;30000quot;); assertEquals(quot;Out of my mind... : category adoptionquot;, selenium.getTitle()); selenium.click(quot;link=We Are Out!quot;); for (int second = 0;; second++) { if (second >= 60) fail(quot;timeoutquot;); try { if (selenium.isTextPresent(quot;Commentsquot;)) break; } catch (Exception e) {} Thread.sleep(1000); } assertTrue(selenium.isTextPresent(quot;Trackbacksquot;)); } } Wednesday, August 13, 2008 17
  18. 18. Generated Java from Must inherit SeleneseTestCase package com.example.tests; import com.thoughtworks.selenium.*; import java.util.regex.Pattern; public class NewTest extends SeleneseTestCase { public void setUp() throws Exception { setUp(quot;http://fredjean.net/quot;, quot;*chromequot;); } public void testNew() throws Exception { selenium.open(quot;/quot;); selenium.click(quot;link=Adoptionquot;); selenium.waitForPageToLoad(quot;30000quot;); assertEquals(quot;Out of my mind... : category adoptionquot;, selenium.getTitle()); selenium.click(quot;link=We Are Out!quot;); for (int second = 0;; second++) { if (second >= 60) fail(quot;timeoutquot;); try { if (selenium.isTextPresent(quot;Commentsquot;)) break; } catch (Exception e) {} Thread.sleep(1000); } assertTrue(selenium.isTextPresent(quot;Trackbacksquot;)); } } Wednesday, August 13, 2008 18
  19. 19. Generated Java package com.example.tests; import com.thoughtworks.selenium.*; import java.util.regex.Pattern; Should rename method public class NewTest extends SeleneseTestCase { public void setUp() throws Exception { setUp(quot;http://fredjean.net/quot;, quot;*chromequot;); } public void testNew() throws Exception { selenium.open(quot;/quot;); selenium.click(quot;link=Adoptionquot;); selenium.waitForPageToLoad(quot;30000quot;); assertEquals(quot;Out of my mind... : category adoptionquot;, selenium.getTitle()); selenium.click(quot;link=We Are Out!quot;); for (int second = 0;; second++) { if (second >= 60) fail(quot;timeoutquot;); try { if (selenium.isTextPresent(quot;Commentsquot;)) break; } catch (Exception e) {} Thread.sleep(1000); } assertTrue(selenium.isTextPresent(quot;Trackbacksquot;)); } } Wednesday, August 13, 2008 19
  20. 20. Generated Java package com.example.tests; selenium.this import com.thoughtworks.selenium.*; import java.util.regex.Pattern; selenium.that public class NewTest extends SeleneseTestCase { public void setUp() throws Exception { } selenium.thisandthat setUp(quot;http://fredjean.net/quot;, quot;*chromequot;); public void testNew() throws Exception { selenium.open(quot;/quot;); selenium.click(quot;link=Adoptionquot;); selenium.waitForPageToLoad(quot;30000quot;); assertEquals(quot;Out of my mind... : category adoptionquot;, selenium.getTitle()); selenium.click(quot;link=We Are Out!quot;); for (int second = 0;; second++) { if (second >= 60) fail(quot;timeoutquot;); try { if (selenium.isTextPresent(quot;Commentsquot;)) break; } catch (Exception e) {} Thread.sleep(1000); } assertTrue(selenium.isTextPresent(quot;Trackbacksquot;)); } } Wednesday, August 13, 2008 20
  21. 21. Generated Java package com.example.tests; import com.thoughtworks.selenium.*; import java.util.regex.Pattern; public class NewTest extends SeleneseTestCase { public void setUp() throws Exception { setUp(quot;http://fredjean.net/quot;, quot;*chromequot;); } public void testNew() throws Exception { Where did waitForTextPresent go? selenium.open(quot;/quot;); selenium.click(quot;link=Adoptionquot;); selenium.waitForPageToLoad(quot;30000quot;); assertEquals(quot;Out of my mind... : category adoptionquot;, selenium.getTitle()); selenium.click(quot;link=We Are Out!quot;); for (int second = 0;; second++) { if (second >= 60) fail(quot;timeoutquot;); try { if (selenium.isTextPresent(quot;Commentsquot;)) break; } catch (Exception e) {} Thread.sleep(1000); } assertTrue(selenium.isTextPresent(quot;Trackbacksquot;)); } } Wednesday, August 13, 2008 21
  22. 22. Generated Java Good start Needs some work to be useful Certainly faster than coding by hand Noisy Wednesday, August 13, 2008 22
  23. 23. Groovy package com.example.tests import com.thoughtworks.selenium.* class NewGroovyTest extends SeleneseTestCase { void setUp() { setUp quot;http://fredjean.net/quot;, quot;*chromequot; } void testNew() { selenium.open quot;/quot; selenium.click quot;link=Adoptionquot; selenium.waitForPageToLoad quot;30000quot; assert quot;Out of my mind... : category adoptionquot; == selenium.title selenium.click quot;link=We Are Out!quot; for (second in 0..60) { if (second == 60) fail quot;timeoutquot; if (selenium.isTextPresent(quot;Commentsquot;)) break; sleep(1000) } assert selenium.isTextPresent(quot;Trackbacksquot;) } } Wednesday, August 13, 2008 23
  24. 24. Groovy Less noisy than Java Still repetitive waitForTextPresent is still missing Wednesday, August 13, 2008 24
  25. 25. Metaprogramming Writing of computer programs that write or manipulate other programs (or themselves) as their data. http://en.wikipedia.org/wiki/Metaprogramming Wednesday, August 13, 2008 25
  26. 26. Metaprogramming Increases code expressiveness Allows SMEs to understand the code Domain Specific Languages Wednesday, August 13, 2008 26
  27. 27. Meta Object Protocol Establishes the rules behind method calling in Groovy Provides the hooks to modify your program's behavior invokeMethod propertyMissing methodMissing Wednesday, August 13, 2008 27
  28. 28. Metaclass All Groovy objects have one Can be defined for Java objects Per class vs per instance Allows developers to quot;mutatequot; a class Wednesday, August 13, 2008 28
  29. 29. Delegation For ward method calls to another object Tedious to do in Java Extend delegate Manually code delegation code Almost trivial in Groovy ExpandoMetaClass Wednesday, August 13, 2008 29
  30. 30. Groovy Delegation /** * Called when a method cannot be found in the class * or the meta class for an object or class. * @param name The name of the missing method * @param args The arguments for the method */ void methodMissing(String name, args) { selenium.quot;$namequot;(* args) } /** * Called when a property cannot be found in the class * or the meta class associated with a class or object. * @param name The name of the property */ void propertyMissing(String name) { selenium.quot;$namequot; } Wednesday, August 13, 2008 30
  31. 31. Goodbye Repetition void testDelegation() { open quot;/quot; click quot;link=Adoptionquot; waitForPageToLoad quot;30000quot; assert quot;Out of my mind... : category adoptionquot; == title click quot;link=We Are Out!quot; for (second in 0..60) { if (second == 60) fail quot;timeoutquot; if (isTextPresent(quot;Commentsquot;)) break; sleep(1000) } assert isTextPresent(quot;Trackbacksquot;) } Wednesday, August 13, 2008 31
  32. 32. Performance Hit Wednesday, August 13, 2008 32
  33. 33. Performance Hit Wednesday, August 13, 2008 33
  34. 34. Intercept, Cache, Invoke /** * Called when a method cannot be found in the class * or the meta class for an object or class. * @param name The name of the missing method * @param args The arguments for the method */ void methodMissing(String name, args) { NewGroovyTest.metaClass.quot;$namequot; = { Object varArgs -> delegate.selenium.metaClass.invokeMethod(delegate.selenium, name, varArgs) } selenium.quot;$namequot;(* args) } Wednesday, August 13, 2008 34
  35. 35. Performance Hit Wednesday, August 13, 2008 35
  36. 36. Groovy Delegation Results in cleaner test code Almost trivial to implement in Groovy Performance hit can be mitigated Wednesday, August 13, 2008 36
  37. 37. waitForTextPresent? void testDelegation() { open quot;/quot; click quot;link=Adoptionquot; waitForPageToLoad quot;30000quot; assert quot;Out of my mind... : category adoptionquot; == title click quot;link=We Are Out!quot; for (second in 0..60) { if (second == 60) fail quot;timeoutquot; if (isTextPresent(quot;Commentsquot;)) break; sleep(1000) } assert isTextPresent(quot;Trackbacksquot;) } Wednesday, August 13, 2008 37
  38. 38. waitForTextPresent? void testDelegation() { open quot;/quot; Replaces waitFor... click quot;link=Adoptionquot; waitForPageToLoad quot;30000quot; with a loop assert quot;Out of my mind... : category adoptionquot; == title click quot;link=We Are Out!quot; for (second in 0..60) { if (second == 60) fail quot;timeoutquot; if (isTextPresent(quot;Commentsquot;)) break; sleep(1000) } assert isTextPresent(quot;Trackbacksquot;) } Wednesday, August 13, 2008 38
  39. 39. waitForTextPresent? void testDelegation() { open quot;/quot; click quot;link=Adoptionquot; waitForPageToLoad quot;30000quot; assert quot;Out of my mind... : category adoptionquot; == title click quot;link=We Are Out!quot; for (second in 0..60) { How about if (second == 60) fail quot;timeoutquot; assertTextPresent? if (isTextPresent(quot;Commentsquot;)) break; sleep(1000) } assert isTextPresent(quot;Trackbacksquot;) } Wednesday, August 13, 2008 39
  40. 40. waitForTextPresent? Selenese generates waitFor, verify, and assert methods Java driver doesn't provide them Java -> Explicitly typed language JavaScript -> What's a type? Wednesday, August 13, 2008 40
  41. 41. Wait a minute... JavaScript is a dynamic language... Groovy is a dynamic language... Why not synthesize these methods in Groovy? Wednesday, August 13, 2008 41
  42. 42. Synthetic Methods Methods that don't really exist Grails finder methods Person.findByFirstNameAndAge(...) Wednesday, August 13, 2008 42
  43. 43. Steps to Take Identify synthetic methods Implement behavior Locate actual getter method Wednesday, August 13, 2008 43
  44. 44. Identifying Methods def methodMissing(String name, args) { switch (name) { case ~/waitForNot.*/: return waitForNot(name, args) case ~/waitFor.*/: return waitFor(name, args) case ~/assertNot.*/: assertNot(name, args) break case ~/assert.*/: assertThat(name, args) break case ~/verifyNot.*/: return verifyNot(name, args) case ~/verify.*/: return verifyThat(name, args) default: return createAndCallMethod(name, args) } } Wednesday, August 13, 2008 44
  45. 45. Implement Behavior private waitFor(name, args) { // Make the bold assumption that the time out is the first param. def timeout = args[0] if (timeout instanceof Integer) { args = args[1..args.length - 1].toArray() } else { timeout = 60000 } def methodName = getMethodName(quot;waitForquot;, name); for (i in 0..(timeout / 1000)) { if (quot;$methodNamequot;(* args)) { return true; } sleep(1000) } fail(quot;Timeout occured in $name for $argsquot;) } Wednesday, August 13, 2008 45
  46. 46. Locating Getter def getMethodName(prefix, name) { [quot;isquot;, quot;getquot;].collect { name.replaceFirst(prefix, it) }.find { delegate.selenium.metaClass.respondsTo(delegate.selenium, it) } } Wednesday, August 13, 2008 46
  47. 47. Loop Begone! void testDelegation() { open quot;/quot; click quot;link=Adoptionquot; waitForPageToLoad quot;30000quot; assert quot;Out of my mind... : category adoptionquot; == title click quot;link=We Are Out!quot; waitForTextPresent quot;Commentsquot; assertTextPresent quot;Trackbacksquot; } Wednesday, August 13, 2008 47
  48. 48. Refactor! Move methods to super class methodMissing propertyMissing Supporting methods Group Groovy tests in one suite Wednesday, August 13, 2008 48
  49. 49. GroovierSelenium Extends SeleneseTestCase with methodMissing Allows Groovy users to write tests that almost look like Selenese http://groovierselenium.googlecode.com Licensed under ASLv2.0 Wednesday, August 13, 2008 49
  50. 50. Near Future JUnit 4.5 test runners GroovierSeleniumRunner GroovySuiteRunner @Selenium annotation Wednesday, August 13, 2008 50
  51. 51. NetBeans & Groovy Grails and Groovy Plugin integrated with NetBeans 6.5 Adds Groovy Support to Java Projects Wednesday, August 13, 2008 51
  52. 52. Looking Back Talked about Selenium Leveraging Groovy metaprogramming Delegating to another object Creating synthetic methods GroovierSelenium NetBeans Wednesday, August 13, 2008 52
  53. 53. Book Programming Groovy (Venkat S.) Wednesday, August 13, 2008 53
  54. 54. Links http://groovy.codehaus.org http://groovierselenium.googlecode.com http://www.pragprog.com/titles/vslg/ programming-groovy Wednesday, August 13, 2008 54
  1. A particular slide catching your eye?

    Clipping is a handy way to collect important slides you want to go back to later.

×