Generated Controller Test Cases

November 16, 2008 – 9:04 am

The generated controller scaffolding from Grails is a great tool for learning the framework quickly. It provides examples of controllers, controller actions, simple GORM methods on your domain classes, Groovy Server Pages and tag libraries – everything you need to get started – EXCEPT for how to unit test the stuff.

I’ve always thought a good addition to Grails would be the ability to generate unit tests for all that scaffolding code in your generated controllers. With the advent of testing enhancements coming to Grails 1.1 and the Grails Testing Plugin making it much easier to unit test Grails artifacts, this is more of a reality.

So I buckled down and created a plugin with a template for testing generated controllers. If you’re on Grails 1.0.4, it hooks directly into events in the build system to create the unit test file automagically. If you haven’t upgraded yet and you’re still on Grails 1.0.3, there’s a script you can call to generate the unit test manually.

I decided to make the Testing Plugin a pre-requisite for the “Test Template” plugin, since it makes the test code SO much easier to write. So to get started, run “grails install-plugin testing“, followed by “grails install-plugin test-template“. Here’s an small example:

grails create-app bookstore
cd bookstore
grails install-plugin testing
grails install-plugin test-template
grails create-domain-class Book
grails generate-controller Book (or you could run generate-all Book)

If you have Grails 1.0.4, that last line will generate the controller for you AND also generate a unit test (test/unit/BookControllerUnitTests.groovy). The unit test will have test methods for each controller action, with several test methods for each of the actions that have if/else statements in them.

If you have Grails 1.0.3, you have to manually generate the unit test, which you can do with this line:

grails generate-controller-unit-test Book

Yes, that’s a long command name, but what are you doing still on Grails 1.0.3 anyway? Grails 1.0.4 has been out for like 48 hours – go upgrade already!

Now run your test suite (grails test-app) and watch your newly generated tests scroll by!

The plugin uses the createArtefact method in Init.groovy, so it also handles packages:

grails create-domain-class com.pirgaua.Book
grails generate-controller com.piragua.Book
// -> generates test/unit/com/piragua/BookControllerUnitTests.groovy

And, it also handles the new “uber-generate” feature in 1.0.4 (courtesy of Marcel Overdijk and GRAILS-2946:

grails create-domain-class Book
grails create-domain-class Author
grails create-domain-class Store
grails create-domain-class Cart
// edit your domain classes and add properties, constraints, etc...
grails generate-all "*"
// -> generates controllers, views, and controller unit tests for ALL your domain classes

And you can override the plugin provided template, just run “grails install-test-templates” and modify the template code to suit your own needs (the templates are copied to src/templates/artifacts/test-templates).

Speaking of the next version – the next feature I want to add to the plugin is a way to generate a unit test for testing domain constraints. Stay tuned!

More documentation on the Test Template plugin page at grails.org

Using those _Install.groovy and _Upgrade.groovy scripts

November 15, 2008 – 4:26 pm

So you’ve created your first Grails plugin and notice that you have these _Install.groovy and _Upgrade.groovy files in your plugin scripts directory.  ”What are these for?” you ask, and promptly remove them – but if utilized them correctly, they’re a great way to make sure your plugin works well with whichever version of Grails your clients are using.

For example, I recently updated the Code-Coverage plugin to work with Grails 1.0.4.  ”Events.groovy” is now deprecated, instead we’re supposed to start using “_Events.groovy” (result of JIRA bug 3467).  The Code-Coverage plugin uses an Events.groovy script to exclude the Cobertura jar files from the WAR file so you don’t end up with Cobertura resources in a production environment.  I could just rename the file from Events.groovy to _Events.groovy and release a new version of the plugin – but that would mean that anyone using the latest version of the plugin on Grails versions prior to 1.0.4 wouldn’t get the exclusion feature that Events.groovy provides!  It would be better to find a fix that’s more backwards compatible.

Enter the _Install.groovy and _Upgrade.groovy scripts!  Using the _Install.groovy script, I can add some logic that Grails will execute as part of the “grails install-plugin” command.

String pluginDir = binding.getVariable('code-coveragePluginDir')
 
if (grailsVersion == '1.0.4'){
	Ant.move(file:"${pluginDir}/scripts/Events.groovy", 
		tofile:"${pluginDir}/scripts/_Events.groovy", 
		failonerror:false)
}

In this example, I’m checking to see if the version of Grails is 1.0.4, and if it is, I rename the file and add an underscore to the name.  This means that if someone installs the plugin with Grails 1.0.3, they’ll have an Events.groovy file – but if they have Grails 1.0.4, the file will be renamed to _Events.groovy for them automatically.

Aside: I thought I would be able to use the ${codeCoveragePluginDir} variable but there’s a small bug in the InstallPlugin script that doesn’t set the pluginDir variable correctly (it’s using the directory name instead of getting it from the plugin definition file, e.g. CodeCoveragePlugin.groovy) – so instead I get the variable directly out of the Groovy binding.

The _Upgrade.groovy script works the same way – although in this case I can simply use the “codeCoveragePluginDir” variable that is bound through GrailsScriptRunner to find the correct plugin directory.

if (grailsVersion == '1.0.4'){
	Ant.move(file:"${codeCoveragePluginDir}/scripts/Events.groovy", 
		tofile:"${codeCoveragePluginDir}/scripts/_Events.groovy", 
		failonerror:false)
}

Now if a client of the plugin is using Grails 1.0.3 and upgrades to 1.0.4, the _Upgrade.groovy script will rename Events.groovy to _Events.groovy for them (and therefore they won’t see the nice “Use of ‘Events.groovy’ is DEPRECATED.  Please rename to ‘_Events.groovy’.” warning message every time they run a Grails command.

More information can be found in the Grails Plugin Developer Guide – Plugin Basics section.

Testing Plugin Presentation

November 12, 2008 – 5:40 pm

Last night I presented at the Minneapolis Groovy/Grails User Group meeting about the new Grails Testing Plugin created by Peter Ledbrook and G2One.  You can download the presentation in PDF form and browse through the sample code on GitHub (check out the test/unit directory).  The plugin makes it really simple to write unit tests in Grails and is a huge step in the right direction towards making test driven development a reality for Grails projects.  Check out the examples and give the plugin a try!

Your Grails domain class has more properties than you think!

June 10, 2008 – 4:32 pm

But you probably already knew that – things like ‘id’, ‘version’ and ‘log’ properties are added to every Grails domain class by the framework and are pretty obvious.  But there are a few more that might not be so familiar.

Take a look at this domain class:

class Book {
    String name
    Author author
}

Of course, it has a name and author, and a few more – if I print out new Book().properties.each {println it} the following are revealed (check out the one in bold):

  • author:null
  • authorId:null
  • class:class Book
  • constraints:… (removed for brevity)
  • errors:org.springframework.validation.BeanPropertyBindingResult: 0 errors
  • id:null
  • log:org.apache.commons.logging.impl.Log4JLogger@de9e85
  • metaClass:groovy.lang.ExpandoMetaClass@47ec8e[class Book]
  • name:null
  • version:null

I guess I understand where this is coming from (since Book has an Author) – but it tripped me up a little bit the other day when I was working with a domain builder.  The builder is pretty ‘dumb’ – it basically builds domain classes based on attributes of a map that match up with attributes on a domain class.  So along came a new domain class:

class Author {
    String name
    String authorId
}

which just happens to contain an ‘authorId’ (users wanted to capture an ID separately from the ID Hibernate generates).  When the builder checks the properties against the two domain classes – it finds a match in the Book class because of the ‘authorId’ property and tries to set it – but BOOM!  ’authorId’ is a read-only property (as it should be).  No worries, it’s a quick fix – now the builder just checks to see if the property also has a setter – something like if(dc.metaClass.hasProperty(dc, it)?.setter) - and TADA, we’re back up and running.

Anyway, the next time you’re playing around in Grails console – give it a shot and take a look at the properties your domain classes have – you might be surprised what you find.

The elusive HTML ‘id’ attribute when using <g:form>

April 14, 2008 – 1:07 pm

In most Grails tags, the usual HTML attributes get passed through to the output HTML, for example the ‘class’ attribute below:

<g:textField name="name" class="inputField" value="${book?.name}" />
results in:
<input type="text" class="inputField" name="name" value="" id="name" />

I tried to do this with a g:form tag and passed in an “id” attribute so I could reference the form in javascript. But you can’t pass an id attribute to the g:form tag because the “id” attribute is reserved for the id of the domain object you’re dealing with:

<g:form action="save" method="post" id="bookForm">
results in:
<form action="/form/book/save/bookForm" method="post" >
instead of this (like what I was hoping for):
<form action="/form/book/save" method="post" id="bookForm">

As a workaround I abandoned the g:form tag whenever I needed to pass through an “id” attribute and just wrote a standard HTML <form> tag instead…until I noticed that the “name” attribute of g:form is also output as the “id” in the generated HTML. So now, I just write a g:form tag with a “name” attribute and end up with the result I’m looking for! Here’s the example:

<g:form action="save" method="post" name="bookForm">
results in:
<form action="/form/book/save" method="post" name="bookForm" id="bookForm" >

“bookForm” shows up in the HTML code as both the name and the id of the HTML form tag.