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 {Service Integration test with MetaClass
def discover() {
Project.list().each {
Thread.startDaemon {
jobService.update(it)
}
}
}
}
Here is the integration test I wrote that tests the above Service.
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.void testEmptyProject() {
def called = false
Project.metaClass.static.list = {[]}
Thread.metaClass.static.startDaemon = {Closure c -> c.call()}
JobService.metaClass.update = {called = true}
new ProjectService(jobService: new JobService()).discover()
assertFalse('updateJobs should not have been called', called)
}
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.
2 comments:
Update.
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: http://www.ibm.com/developerworks/java/library/j-pg06239/index.html
Basically instead of defining it like this:
Project.metaClass.static.list = {[]}
A better way is:
Project.metaClass.static.list = {->
[]
}
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. http://stackoverflow.com/questions/12178251/instance-metaclass-changes-leaking-from-instance-to-instance
Post a Comment