Sunday, March 12, 2006

EasyMock 2.0 looks promising

EasyMock 2.0 has gone in a new direction API wise. Instead of dealing with a control object as you did in 1.0, you interact with the framework through static methods imported from a class named EasyMock. I prefer the new DSL-like approach.

Sadly, instead of creating a new package for the incompatible API (easymock2 perhaps?), they prefixed incompatible interface names with an "I" and mixed old and new classes in the same package. Ewww.

But if you can look past the class evolution faux pas (the ugliness doesn't really impose itself on your code afterall), EasyMock has embraced JDK 1.5 generics to take static type checking for mocks to a whole new level.

First, creating a mock. They got it half right:

static <T> T createMock(Class<T> toMock)
That removes the need to cast in simple cases, but, thanks to erasure, breaks for more complex cases (T = List<String> for example). I think they really want:
static <T> T createMock(Class<? super T> toMock)
EasyMock has always had good type checking when it comes to specifying what methods you expect your code to invoke. As before, you create a mock, invoke methods you expect your code to invoke with expected parameters, swich over to replay mode, and run your test code:
Foo mockFoo = createMock(Foo.class);
// code you expect to invoke doSomething().
Going in today, foremost on my mind was how would I tell EasyMock what to return from Foo.doSomething()? To my pleasant surprise, not only can you specify a return value, but the code is statically typed! Say for a moment that doSomething() returns an int. We can instruct EasyMock to return 5 from doSomething() like so:
Foo mockFoo = createMock(Foo.class);
// code which expects doSomething() to return 5.

To our benefit, the compiler verifies that that the types returned from doSomething() and passed to andReturn() match.

Before JDK 1.5 and generics came along, I said that no mocking framework was compelling enough for me to abandon plain Java mocking. EasyMock came close even then, but now it looks like I'll really have to change my tune. I'll test drive EasyMock in some real world tests and get back to you.


Blogger colt_nz said...

I'd be interested to know your thoughts on JMock if youve seen it.

3:44 PM  
Blogger Bob said...

Yuck. Not a fan at all. I hate the idea of using method names in strings. From the jMock web site (comparing jMock to EasyMock):

"We have found that the use of strings in jMock is not a huge disadvantage, especially if you use an IDE that can apply refactorings to names in string constants. Often when doing TDD, the method named in an expectation will not yet be defined in any interface, and so code completion cannot be used. We have found that the flexibility1 allowed by the jMock API is better at reducing the work of refactoring than the use of direct method calls."

Bullshit. I don't want to create a corresponding constant for every method I mock. If I'm doing TDD (I don't), even if I code to a method that doesn't exist yet, my IDE can generate the target method for me based on my call. That won't work with JMock.

EasyMock code is more readable and the type checking and IDE support is *much* better. I can't imagine what "flexibility" could possibly justify the tradeoffs.

Generics have changed the playing field. EasyMock's API has stood up well while jMock's has not.

3:57 PM  
Anonymous Anonymous said...

Hello cracybob, can you explain why

static <T> T createMock(Class<? super T> toMock)

is better than

static <T> T createMock(Class<T> toMock)

? I don't understand how you would be able
to define a mock for T = List<String> using this syntax.

3:06 PM  
Blogger Bob said...

Damn. The problem with Class<T> is that you get an unchecked assignment warning in the client code. If we use Class<?>, we get rid of the warning, but you don't have any type checking anymore (i.e. checking that toMock is the raw class for the type you're instantiating). Based on a slightly different experience I had, I wrongly assumed that Class<? super T> would return the right type with a little more checking than Class<?>. Unfortunately, it behaves the same way as Class<T> (i.e. it returns List instead of List<String> so you get a warning). I'm not sure which way I'd go, Class<T> or Class<?>. Those who designed Java generics would just tell you to use a factory instead of a Class, but that's obviously not an option. I really hate they way erasure was handled with Class. Maybe allowing things like Class<List<String>> would not have been 100% "correct," but I'm starting to think it would have been more pragmatic.

While I have you, it would also be cool if I could call replayAll() and verifyAll() instead of "replay(foo); replay(bar); replay(tee)...". replayAll() would replay all of the mocks I've created so far. To keep tests from interfering with each other, I would call clearAll() from my tearDown() method.

10:29 AM  
Anonymous Anonymous said...

Thank you for your helpful comment on jMock. So, you don't use it but you know it won't work in practice and all the people who do must be misguided fools? And what's this stuff about creating constants?

It's true that generics might change the game, but there are plenty of sites out there that are still not allowed to use Java 5, so we're staying conservative for now.

Historically the various mock libraries have leapfrogged each other to improve the state of the art. Tammo pinches ideas from us and vice-versa. Nat Pryce has done some nice work in C# that we're trying to figure out how to port to Java.

2:29 AM  
Blogger Bob said...

No, I don't use it, and I won't. Why would I give up things like type checking, auto completion, and automated refactoring when I can use EasyMock and not have to?

I've provided some simple code samples. How about instead of claiming that jMock will somehow magically leapfrog EasyMock one day, you show how jMock can make those samples cleaner, easier to read, and more maintainable?

10:53 AM  

Post a Comment

<< Home