Versions Compared

Key

  • This line was added.
  • This line was removed.
  • Formatting was changed.
Comment: Migrated to Confluence 4.0

This page discusses

Excerpt

how to use NekoHTML, HtmlUnit, Watij and WebTest to test web applications

.
There are many ways to test Web Applications with Groovy:

  • use Groovy (potentially in conjunction with a specialist HTML parser) to parse HTML pages as if they were XML
  • use Groovy to simplify the code required to drive a Java API browser simulator, e.g. HtmlUnit or HttpUnit
  • use Groovy to simplify the code required to drive a Java API for manually driving a real browser, e.g. IE or Firefox
  • use Groovy to interact with a higher-level testing library which uses one of the above two approaches, e.g. Watij (for the equivalent of Watir in the Ruby world) or WebTest (to open up the possibility of testing more than just web applications)

We examine a few approaches below.

Groovy with CyberNeko HTML Parser

NekoHTML is a library which allows you to parse HTML documents (which may not be well-formed) and treat them as XML documents (i.e. XHTML). NekoHTML automatically inserts missing closing tags and does various other things to clean up the HTML if required - just as browsers do - and then makes the result available for use by normal XML parsing techniques.

Here is an example of using NekoHTML with XmlParser to find '.html' hyperlinks on the groovy homepage:

Code Block
def parser = new org.cyberneko.html.parsers.SAXParser()
parser.setFeature('http://xml.org/sax/features/namespaces', false)
def page = new XmlParser(parser).parse('http://groovy.codehaus.org/')
def data = page.depthFirst().A.'@href'.grep{ it != null && it.endsWith('.html') }
data.each { println it }

We turned off namespace processing which lets us select nodes using '.A' with no namespace.

Here is one way to do the same example with XmlSlurper:

Code Block
def page = new XmlSlurper(new org.cyberneko.html.parsers.SAXParser()).parse('http://groovy.codehaus.org/')
def data = page.depthFirst().grep{ it.name() == 'A' && it.@href.toString().endsWith('.html') }.'@href'
data.each { println it }

We didn't turn off namespace processing but do the selection using just the local name, i.e. '.name()'.

Here is the output in both cases:

Code Block
none
none
http://groovy.codehaus.org/apidocs/index.html
/faq.html
/groovy-jdk.html
http://groovy.codehaus.org/team-list.html
http://groovy.codehaus.org/xref/index.html
http://www.javamagazin.de/itr/ausgaben/psecom,id,317,nodeid,20.html
http://www.weiqigao.com/blog/2006/09/14/gruby_on_grails_tonight_at_630.html
http://www.oreillynet.com/onjava/blog/2006/09/charles_nutter_responds_our_fu.html

Now that we have the links we could do various kinds of assertions, e.g. check the number of links, check that a particular link was always on the page, or check that there are no broken links.

Groovy and HtmlUnit

The following example tests the Google search engine using HtmlUnit:

Code Block
import com.gargoylesoftware.htmlunit.WebClient

def webClient = new WebClient()
def page = webClient.getPage('http://www.google.com')
// check page title
assert 'Google' == page.titleText
// fill in form and submit it
def form = page.getFormByName('f')
def field = form.getInputByName('q')
field.setValueAttribute('Groovy')
def button = form.getInputByName('btnG')
def result = button.click()
// check groovy home page appears in list (assumes it's on page 1)
assert result.anchors.any{ a -> a.hrefAttribute == 'http://groovy.codehaus.org/' }

Note that to use HtmlUnit with Groovy you should not include the xml-apis-*.jar included with HtmlUnit in your CLASSPATH.

Groovy and Watij

The following example tests the Google search engine using Watij:

Code Block
import watij.runtime.ie.IE
import watij.finders.SymbolFactory

def ie = new IE()
ie.start('http://www.google.com')
// check page title
assert ie.title() == 'Google'
// fill in query form and submit it
ie.textField(SymbolFactory.@name, 'q').set('Groovy')
ie.button(SymbolFactory.@name, 'btnG').click()
// check groovy home page appears in list by trying to flash() it
ie.link(SymbolFactory.url, 'http://groovy.codehaus.org/').flash()
ie.close()

You can use Watij from within groovysh or groovyconsole if you want to have an interactive (irb-like in ruby terms) experience.

Groovy and WebTest

The following example tests the Google search engine using WebTest:

Code Block
xml
xml
<webtest name="google test">
    <steps>
        <invoke url="http://google.com"/>
        <verifyTitle text="Google"/>
        <setInputField name="q" value="Groovy"/>
        <clickButton name="btnG"/>
        <verifyXPath xpath="//a[@href='http://groovy.codehaus.org/']" />
    </steps>
</webtest>

The above fragment can be inserted into any Ant build script where we have defined the WebTest tasks.

The above example didn't use any Groovy but we could have just as easily used some Groovy for the last line if we didn't like the XPath expression, for example:

Code Block
xml
xml
<webtest name="google test">
    <steps>
        <invoke url="http://google.com"/>
        <verifyTitle text="Google"/>
        <setInputField name="q" value="Groovy"/>
        <clickButton name="btnG"/>
        <groovy>
        assert step.context.currentResponse.anchors.any{ a -> a.hrefAttribute == 'http://groovy.codehaus.org/' }
        </groovy>
    </steps>
</webtest>

Depending on your setup, you could produce the following report for this test:

Alternatively, we could have written the whole test in Groovy using AntBuilder as follows:

Code Block
def webtest_home = System.properties.'webtest.home'
def ant = new AntBuilder()

ant.taskdef(resource:'webtest.taskdef'){
    classpath(){
           pathelement(location:"$webtest_home/lib")
           fileset(dir:"$webtest_home/lib", includes:"**/*.jar")
    }
}

ant.webtest(name:'Test Google with Groovy, AntBuilder and WebTest'){
    steps(){
        invoke(url:'http://www.google.com')
        verifyTitle(text:'Google')
        setInputField(name:'q', value:'Groovy')
        clickButton(name:'btnG')
        verifyXPath(xpath:"//a[@href='http://groovy.codehaus.org/']")
    }
}

Grails can automatically create this style of test for your generated CRUD applications, see Grails Functional Testing for more details.