Groovier Selenium (Djug)
Upcoming SlideShare
Loading in...5
×
 

Groovier Selenium (Djug)

on

  • 3,834 views

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

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

Statistics

Views

Total Views
3,834
Views on SlideShare
3,449
Embed Views
385

Actions

Likes
5
Downloads
129
Comments
1

9 Embeds 385

http://blog.fredjean.net 299
http://fredjean.net 50
http://reddyphp.wordpress.com 19
http://localhost 11
http://www.slideshare.net 2
http://www.agglom.com 1
http://feeds.feedburner.com 1
http://www.linkedin.com 1
https://www.linkedin.com 1
More...

Accessibility

Categories

Upload Details

Uploaded via as Adobe PDF

Usage Rights

CC Attribution-NonCommercial-ShareAlike LicenseCC Attribution-NonCommercial-ShareAlike LicenseCC Attribution-NonCommercial-ShareAlike License

Report content

Flagged as inappropriate Flag as inappropriate
Flag as inappropriate

Select your reason for flagging this presentation as inappropriate.

Cancel

11 of 1

  • Full Name Full Name Comment goes here.
    Are you sure you want to
    Your message goes here
    Processing…
  • nice work
    Are you sure you want to
    Your message goes here
    Processing…
Post Comment
Edit your comment

    Groovier Selenium (Djug) Groovier Selenium (Djug) Presentation Transcript

    • Groovier Selenium Denver JUG 8/13/2008 Frederic Jean fred@fredjean.net frederic.jean@sun.com Wednesday, August 13, 2008 1
    • who am i Wednesday, August 13, 2008 2
    • 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
    • Selenium UI testing tool Runs in browser Well suited for Ajax applications Wednesday, August 13, 2008 4
    • Selenium IDE Firefox plugin Simplifies writing and testing Selenese test case Can record and play back Selenese tests Wednesday, August 13, 2008 5
    • Selenese HTML Tables Action Target Value Intepreted by Selenium Core Actions match JavaScript functions Wednesday, August 13, 2008 6
    • Example BlogTest open / clickAndWait link=Adoption Out of my mind... : category assertTitle adoption Wednesday, August 13, 2008 7
    • Selenese Locators Allows an action to target a specific DOM element on the page <type>=<locator> Wednesday, August 13, 2008 8
    • 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
    • Sample Test Wednesday, August 13, 2008 10
    • Selenese TestSuites Groups and organizes individual Selenese tests Can be run through ant Wednesday, August 13, 2008 11
    • 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
    • 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
    • Generating Java Test Wednesday, August 13, 2008 14
    • 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
    • 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
    • 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
    • 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
    • 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
    • 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
    • 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
    • Generated Java Good start Needs some work to be useful Certainly faster than coding by hand Noisy Wednesday, August 13, 2008 22
    • 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
    • Groovy Less noisy than Java Still repetitive waitForTextPresent is still missing Wednesday, August 13, 2008 24
    • 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
    • Metaprogramming Increases code expressiveness Allows SMEs to understand the code Domain Specific Languages Wednesday, August 13, 2008 26
    • 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
    • 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
    • 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
    • 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
    • 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
    • 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 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
    • Performance Hit Wednesday, August 13, 2008 35
    • Groovy Delegation Results in cleaner test code Almost trivial to implement in Groovy Performance hit can be mitigated Wednesday, August 13, 2008 36
    • 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
    • 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
    • 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
    • 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
    • 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
    • Synthetic Methods Methods that don't really exist Grails finder methods Person.findByFirstNameAndAge(...) Wednesday, August 13, 2008 42
    • Steps to Take Identify synthetic methods Implement behavior Locate actual getter method Wednesday, August 13, 2008 43
    • 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
    • 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
    • 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
    • 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
    • Refactor! Move methods to super class methodMissing propertyMissing Supporting methods Group Groovy tests in one suite Wednesday, August 13, 2008 48
    • 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
    • Near Future JUnit 4.5 test runners GroovierSeleniumRunner GroovySuiteRunner @Selenium annotation Wednesday, August 13, 2008 50
    • NetBeans & Groovy Grails and Groovy Plugin integrated with NetBeans 6.5 Adds Groovy Support to Java Projects Wednesday, August 13, 2008 51
    • Looking Back Talked about Selenium Leveraging Groovy metaprogramming Delegating to another object Creating synthetic methods GroovierSelenium NetBeans Wednesday, August 13, 2008 52
    • Book Programming Groovy (Venkat S.) Wednesday, August 13, 2008 53
    • Links http://groovy.codehaus.org http://groovierselenium.googlecode.com http://www.pragprog.com/titles/vslg/ programming-groovy Wednesday, August 13, 2008 54