Ola Bini is a Swedish developer working for ThoughtWorks. His daily job includes working on JRuby, starting up a Swedish ThoughtWorks office and mucking around with Java and Ruby. In his spare time he spends most time on his language Ioke, working on one of several other open source projects or reading science fiction. Ola has presented at numerous conferences, such as JavaOne, Javapolis, JAOO, RailsConf, TheServerSide Java Symposium and more. He is the author of APress book Practical JRuby on Rails Ola is a DZone MVB and is not an employee of DZone and has posted 45 posts at DZone. You can read more from them at their website. View Full User Profile

Java and Mocking

07.04.2008
| 15199 views |
  • submit to reddit

I've just spent my first three days on a project in Leeds. It's a pretty common Java project, RESTful services and some MVC screens. We have been using Mockito for testing which is a first for me. My immediate impression is quite good. It's a nice tool and it allows some very clean testing of stuff that generally becomes quite messy. One of the things I like is how it uses generics and the static typing of Java to make it really easy to make mocks that are actually type checked; like this for example:

Iterator iter = mock(Iterator.class);
stub(iter.hasNext()).toReturn(false);

 

// Call stuff that starts interaction

verify(iter).hasNext();

 

These are generally the only things you need to stub stuff out and verify that it was called. The things you don't care about you don't verify. This is pretty good for being Java, but there are some problems with it too. One of the first things I noticed I don't like is that interactions that isn't verified can't be disallowed in an easy way. Optimally this would happen at the creation of the mock, instead of actually calling the verifyNoMoreInteractions() afterwards instead. It's way to easy to forget. Another problem that quite often comes up is that you want to mock out or stub some methods but retain the original behavior of others. This doesn't seem possible, and the alternative is to manually create a new subclass for this. Annoying.

Contrast this to testing the same interaction with Mocha, using JtestR, the difference isn't that much, but there is some missing cruft:

iter = mock(Iterator)
iter.expects(:hasNext).returns(false)
# Call stuff that starts interaction

Ruby makes the checking of interactions happen automatically afterwards, and so you don't have any types you don't need to care about most stuff the way you do in Java. This also shows a few of the inconsistencies in Mockito, that is necessary because of the type system. For example, with the verify method you send the mock as argument and the return value of the verify-method is what you call the actual method on, to verify that it's actually called. Verify is a generic method that returns the same type as the argument you give to it. But this doesn't work for the stub method. Since it needs to return a value that you can call toReturn on, that means it can't actually return the type of the mock, which in turn means that you need to call the method to stub before the actual stub call happens. This dichotomy gets me every time since it's a core inconsistency in the way the library works.

Contrast that to how a Mockito like library might look for the same interaction:

iter = mock(Iterator)
stub(iter).hasNext.toReturn(false)
# Do stuff

verify(iter).hasNext

The lack of typing makes it possible to create a cleaner, more readable API. Of course, these interactions are all based on how the Java code looked. You could quite easily imagine a more free form DSL for mocking that is easier to read and write.

Conclusion? Mockito is nice, but Ruby mocking is definitely nicer. I'm wondering why the current mocking approaches doesn't use the method call way of defining expectations and stubs though, since these are much easier to work with in Ruby.

Also, it was kinda annoying to upgrade from Mockito 1.3 to 1.4 and see half our tests starting to fail for unknown reasons. Upgrade cancelled. 

 

Published at DZone with permission of Ola Bini, author and DZone MVB.

(Note: Opinions expressed in this article and its replies are the opinions of their respective authors and not those of DZone, Inc.)

Tags:

Comments

LOL DUDE replied on Sat, 2008/07/05 - 4:31am

How about combining a nice mocking language with your java classes with ... Groovy?

def mock = new MockFor(YourJava.class)
class Example {
def bla = { return new YourJavaClass().someField }
}

def mock = new MockFor(YourJavaClass)

mock.demand.getSomeField { "two" }
mock.use {
def c = new Example()
assert c. bla() == "two"
}

LOL DUDE replied on Sat, 2008/07/05 - 4:51am in response to: LOL DUDE

Sorry for the indentation, but either it is impossible to post correctly intended code with this editor or I am way too "ease-of-use"-spoiled from being a Mac-User to actually fight my way through this.

LOL DUDE replied on Sat, 2008/07/05 - 8:21am

Oh sorry, I forgot to see that this was posted in the ruby section. Forget my advice then.

Jess Holle replied on Sat, 2008/07/05 - 8:58pm

You might want to look at a more powerful mocking tool, e.g. jmockit.

Comment viewing options

Select your preferred way to display the comments and click "Save settings" to activate your changes.