Test-driven development (TDD) is handy if used sensibly, and we’re feeling the need to make our automated tests a little broader. The Grails site has great documentation on setting up tests for Controllers and Services, but I couldn’t find a decent explanation of how to set up tidy automated tests for GSPs… so without further ado this is what I did.
I want tests to be:
- Easy to write (and hence read)
- Low maintenance, i.e. I don’t want to have to update tests whenever I make changes that aren’t important to the test
- Good at picking up unexpected behaviour rather than changes to HTML structure
Running your GSP from TestApp
Grails ships with a handy class called GroovyPagesTestCase
which allows your test to get the output of a given GSP file given a defined input, like this:
def file = new File("grails-app/views/myview.gsp") def model = [someVariable:12345] def htmlString = applyTemplate( file.text, model )
We’re passing in the text from the GSP file as a template, along with a model comprised of whatever variables and mock objects the view should need.
Now I’ve got a string containing a bunch of HTML, okay, that’s the right direction but my lazy gene is not satisfied yet.
Note 1: If your template calls other templates then it makes life easier to use absolute URLs, e.g. templateUrl"/>
Note 2: This method assumed you’ll specify an explicit model for each sub-view, e.g. myObj:myObj]}"/>
From a sticky HTML mess to something useful
The easiest way to get from unparsed HTML to a useful searchable structure seems to be Groovy’s XMLSlurper. It parses XML rather than HTML by default, but you can instantiate it with a more HTML-friendly parser like TagSoup like so:
def slurper = new XmlSlurper( new org.ccil.cowan.tagsoup.Parser() ) def parsedHtml = slurper.parseText(text)
Easy.
Pulling it all together
import grails.test.GroovyPagesTestCase import org.ccil.cowan.tagsoup.Parser class GspXyzTests extends GroovyPagesTestCase { boolean transactional = false def slurper = new XmlSlurper( new Parser() ) // This test looks for a specific thing in the resulting parsed HTML void testSomeImportantOutput() { //Open file containing GSP under test def file = new File("grails-app/views/myfolder/template.gsp") assertNotNull(file) //Obtain result of GSP page def text = applyTemplate(file.text, [ pagenumber:123, pagesize:10, someMockObject:[ foo:"bar", nestedMockObject:[ [id:12345], [id:67890] ] ] ]) def html = slurper.parseText( text ) // test some aspect of the parsed structure, the trick would be to make the test resilient to a degree of cosmetic change assertEquals 1, html.head.meta.list().findAll{ it.@name?.text().toLowerCase() == "robots" }.size() } }