I've just started on a new project and of course we want to ensure all, and I really mean all, our code is tested. Now, this being a Java project many people assume 80% is pretty good and that getting upwards of 90% coverage and beyond takes a superhuman effort. In the past I would have agreed. Now that I am learning Groovy, however, I no longer agree and now think it is not only possible to get above 90% coverage and close to 100%, but quite easy.
First some background on why normally 80% is acceptable. Well, it is simply that Java is not very flexible when it comes to mocking certain types of objects. For example, try mocking a standard JDK class that is marked as final and making it do your bidding. Not easy. It is easy to mock classes that implement an interface and even non-final classes using things like jMock and EasyMock.
Let's take an example. We have some reflective code that uses the java.lang.reflect.Field class' get() method. This method throws IllegalArgumentException and IllegalAccessException, the latter of which is a checked exception. Since there is nothing meaningful we can really do about a IllegalAccessException we catch it and rethrow as an IllegalStateException, which is a runtime (unchecked) exception. The problem is that covering the case when an IllegalAccessException is thrown is not something you can mock using a tool like EasyMock, even the cool EasyMock Class Extension, since the class is marked final.
Last week I found something called jmockit which is very cool and essentially uses some Java 5 class instrumentation magic (you must specify a javaagent to the JVM when running your tests) to replace byte code at runtime. This allows you to literally mock anything at all, except for the minor issue that if you want to mock methods in a JDK class like Field all the test classes must be loaded by the boot class loader, which means you have to use the -Xbootclasspath startup option. This starts to really become intrusive, complicate your build process, and generally make things a lot more complex. Even needing to run the JVM with -javaagent:jmockit.jar is intrusive and somewhat complicated.
Enter Groovy. I am a total beginner at Groovy having only attended some sessions at various conferences like No Fluff Just Stuff and JavaOne, a little tinkering with the Groovy shell, and having purchased Groovy in Action. Within an hour or so of tinkering I was able to produce an IllegalAccessException from a mocked Field using Groovy. The following code is probably crap (again being a Groovy newbie) but is ridiculously simple and readable nonetheless:
import groovy.mock.interceptor.MockFor
import java.lang.reflect.Field
class SimpleUnitTest extends GroovyTestCase {
void testMockingField() {
def fieldMock = new MockFor(Field)
def mb = new MyBean()
fieldMock.demand.get(mb) { throw new IllegalAccessException("haha") }
fieldMock.use {
Field f = MyBean.class.getDeclaredField("name")
shouldFail(IllegalAccessException) {
f.get(mb)
}
}
}
}
class MyBean {
def name = "hello, groovy"
}
That's it. It is ridiculous how easy Groovy makes mocking any object. The steps are basically:
- Create mock using
MockFor - Tell mock what to do using
demand - Execute code under test in a closure passed to
use
Developers used to the power of dynamic languages like Ruby of course won't think too much of this as it is even simpler to create mocks, but this is something new for Java developers. Now there is really almost no reason why you can't test literally all of your Java code, and more importantly, do it very easily.
5 Comments
Leave a comment
0 TrackBacks
Listed below are links to blogs that reference this entry: Dirt Simple Mock Objects In Groovy.
TrackBack URL for this entry: http://www.nearinfinity.com/mt/mt-tb.cgi/417



what is the syntax for handling multiple mocks? Do you use nested
"use" blocks? That could get a little messy.
It seems like error handling code might be an obstacle to getting 100% coverage. Errors caused by invalid variable values, 3rd party library failures, network problems, etc can be tough to intentionally reproduce.
Jason, I have no idea what the syntax is for multiple mocks, since I've just started learning Groovy. :-) But the nice thing is that since you can pretty much write normal Java code in Groovy, if it turned out that Groovy could not handle this elegantly you can still use EasyMock where multiple mocks is pretty simple.
Seth, my example is introducing essentially a tough to reproduce error condition, i.e. getting a Field to throw an IllegalAccessException without mucking about with the policy file and such. As for things like 3rd party libraries, same thing. You can, it seems, literally mock anything you want, just like you can do in Ruby. So you can mock a network connection and simulate a failure, or mock Hibernate and simulate a database failure, etc.
Another option might be to use closures instead of some simple mocks. The page gets pretty indepth, but here's a neat tidbit:
new Thread(
{println "running"} as Runnable
).start()
I really like that syntax!
http://groovy.codehaus.org/Developer+Testing+using+Closures+instead+of+Mocks
http://groovy.codehaus.org/Groovy+way+to+implement+interfaces