Friday, July 11, 2008

Groovy Threads and MetaClass example

* Update - the code has been updated. The original test was incorrect and was producing a false positive. What you see now is the correct way.

It's been awhile since I have been able to play around with grails/groovy. Now, instead of using a pretend app to learn grails/groovy, I have teamed up with Jeff Black, Chad Gallemore, and Sam Jones (fellow office co-workers here in Joplin, MO) to rewrite an existing small internal java webapp using grails. It's a perfect application for grails and so far we are loving it.

Early on, we needed to figure out 2 things:
1) Groovy way of creating Threads
2) Writing an integration test for a Groovy Service

Groovy Threads
Below is a summary of our service that shows how to start new threads in groovy.

class ProjectService {
def discover() {
Project.list().each {
Thread.startDaemon {
Service Integration test with MetaClass
Here is the integration test I wrote that tests the above Service.
void testEmptyProject() {
def called = false

Project.metaClass.static.list = {[]}
Thread.metaClass.static.startDaemon = {Closure c ->}
JobService.metaClass.update = {called = true}
new ProjectService(jobService: new JobService()).discover()

assertFalse('updateJobs should not have been called', called)

The first missing key for me was the static keyword on metaClass since in the service I am calling Project.list() and Thread.startDaemon(). The second mystery was how to mock out Thread.startDaemon() since there could be, and was, a race condition between the update closure setting called = true and my assertFalse.

Thanks Chad for the suggestion of using metaClass. I also got a lot of help from Glenn Smith's blog about testing controllers and Dustin's groovy thread example.


Gerardo said...

Unfortunately, Groovy does not support mocking of static methods with its MockFor implementation. For those needing to test static methods with Groovy Mocks, I provide my own implementation of StaticMockFor here:

James Lorenzen said...

Looks like there is a better way to define the closure when overriding methods. I was struggling recently to get this to work and found this article that helped me solve my problem:

Basically instead of defining it like this:
Project.metaClass.static.list = {[]}

A better way is:
Project.metaClass.static.list = {->

jlorenzen said...

If you run into issue using metaClass to mock out classes in a test, and the test has multiple test methods that mock out the same class, take a look at this article if the method isn't getting replaced.