Very quick first impression of Mockito (compared to JMock)

By YopY on Tuesday 11 January 2011 21:00 - Comments (9)
Category: -, Views: 11.414

Case: An object that has a list of Command object, each Command listening to one or more String inputs (think console application). The object under test has two methods: a setCommands() method, registering a List of Commands with the object, and a commandEntered() method that takes any String input.

The commandEntered method has to do a few things:

* Clean up the entered command (trim)
* Find a command that listens to the command, using the Command object's listensTo() method
* Call the Command's execute() method, passing it the entered (cleaned) command and the application state.

Ideal case for a mock object, where the Command object gets mocked. Purpose of the mock object is to return information when called (particularly the listensTo() method), and to verify its execute() method was called with the proper arguments.

Below are two unit tests that assert the same things. Two commands are used, one Command that listens to the input, one that doesn't - this to verify the method doesn't call the execute() method on the Command that does not listen to the input. The first test is how I used to write tests, using JMock:


Java:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
@Test
public void testCommandEnteredJMock() {
    Mockery context = new Mockery();
    // objects used inside anonymous inner classes must be final.
    final Command command = context.mock(Command.class);
    // have to explicitly give the other command a name due to name clashing.
    final Command otherCommand = context.mock(Command.class, "otherCommand");
    final CommandListener commandListener = new CommandListener();
    final String actualCommandString = "test";
    String commandStringWhitespaced = "\ttest  ";
    
    // set up JMock expectations
    context.checking(new Expectations() {{
        oneOf(otherCommand).listensTo(actualCommandString); will(returnValue(false));
        oneOf(command).listensTo(actualCommandString); will(returnValue(true));
        oneOf(command).execute(actualCommandString, game);
        never(otherCommand).execute(actualCommandString, game);
    }});
    
    commandListener.setCommands(Arrays.asList(otherCommand, command));
    commandListener.commandEntered(commandStringWhitespaced);
    // jmock does not by default test if all the expectations were met.
    context.assertIsSatisfied();
}



So, tonight I went to write a unit test for the CommandListener (let's just call it that for now), and found out that in the archetype generated by Maven, Mockito was used by default instead of JMock. So, I figured why the heck not, heard lots of positive things, and just went ahead with Mockito. Turns out it's really easy to learn. Here's roughly the same unit test (or, tests the same thing) using Mockito:


Java:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
@Test
public void testCommandEnteredMockito() {
    // create mock objects.
    Command command = mock(Command.class);
    Command otherCommand = mock(Command.class);
    State programState = mock(ProgramState.class);
    String commandStringWhitespaced = "\ttest  ";
    String actualCommandString = "test";

    // set up expectations
    when(command.listensTo(actualCommandString)).thenReturn(true);
    when(otherCommand.listensTo(actualCommandString)).thenReturn(false);

    // run the methods
    CommandListener commandListener = new CommandListener();
    commandListener.setCommands(Arrays.asList(otherCommand, command));
    commandListener.commandEntered(commandStringWhitespaced);

    // Verify the right methods were called in the right manner.
    verify(command).listensTo(actualCommandString);
    verify(otherCommand).listensTo(actualCommandString);

    // verify execute method was called properly on the given command, and
    // other command is never executed.
    verify(command).execute(actualCommandString, programState);
    verify(otherCommand, never()).execute(actualCommandString, programState);
}



In terms of lines of code, there's little difference - both use about 15 loc. JMock would actually be a bit shorter (like, one line) because you'd usually have the Mockery object as a class field. You could also save a few lines by inlining the input string with whitespace. But, these are unit tests, most important of those is that they should be easy to read and comprehend. I like using variables. LOC mean nothing.

What does mean something is the amount of typing you have to do to set up either. Writing unit tests are, at least in my perception, comparable to playing an MMO - grinding like crazy. You will start to regret writing things like context.checking(new Expectations() {{}}); over and over again, even if you put them under an Eclipse template. Worse, when you accidentally run a code formatter over your unit tests, it'll turn into something like:


Java:
1
2
3
4
5
6
7
8
9
10
11
// set up JMock expectations
        context.checking(new Expectations() {
            {
                oneOf(otherCommand).listensTo(actualCommandString);
                will(returnValue(false));
                oneOf(command).listensTo(actualCommandString);
                will(returnValue(true));
                oneOf(command).execute(actualCommandString, programState);
                never(otherCommand).execute(actualCommandString, programState);
            }
        });



which is nigh-on illegible. JMock's expectations rely in part on manual formatting to be legible and make sense. I did read about people changing their syntax highlighting to hide dots and braces, turning JMock's expectations into a readable story without Java's syntax getting in the way, but that still relies on keeping a proper syntax. I'm sure you can change the formatter's rules for unit tests, though. Still, that's a lot of code.

Mockito's mock code remain completely unaltered after a test run, and, at least based on my current (and very short) experiences, are just as legible as JMock's code - maybe even better, because there's a lot less boilerplate code. Compare (in story mode):

One of command listens to actual command will return value false
One of command execute(s) actual command, program state
(assert is satisfied)

with:

When command listens to actual command, then return false
Verify command execute(s) actual command, program state

I'm kinda starting to like Mockito, really. With JMock, both expectations and 'verifications' (i.e. verify that certain methods were called) are chucked into an Expectations object. This is better separated in Mockito - tests are more clearly separated into expectations, running, and verifying stages.

I approve.

Volgende: EXI: The last binary standard? 03-'11 EXI: The last binary standard?
Volgende: Quick tryouts: Websockets 01-'11 Quick tryouts: Websockets

Comments


By Willem, Tuesday 11 January 2011 21:03

Waarom in het Engels.

By Tweakers user Juicy, Tuesday 11 January 2011 21:06

Dat zie je regelmatig ... Waarom dat is vraag ik me nog steeds af. Ik zie buitenlanders namelijk niet snel op een nederlandse site rondneuzen op zoek naar engelse blogs.

By Tweakers user CodeCaster, Tuesday 11 January 2011 21:09

Je Engels oefenen? En Tweakblogs wordt bijzonder goed ge´ndexeerd door zoekmachines. :)

By Tweakers user YopY, Tuesday 11 January 2011 21:15

Waarom niet? :+ ik ken een halve bakkes aan Engels-sprekende mensen, die begrijpen het anders niet. En misschien hebben meer mensen er iets aan als ik het in het engels doe - ik heb zelf nog nauwelijks goeie resources gevonden in het nederlands. Snelle steekproef: er is gewoon niks in het nederlands. Misschien iets als phpfreaks.nl of iets op GoT, maar daar houdt het wel mee op.

By Tweakers user RobIII, Tuesday 11 January 2011 21:29

YopY wrote on Tuesday 11 January 2011 @ 21:15:
...goeie resources...Misschien iets als phpfreaks.nl...
:( Ga ergens anders lopen schelden :(

:+

By Tweakers user Ch3cker, Tuesday 11 January 2011 21:40


code:
1
2
3
// set up JMock expectations 
        context.checking(new Expectations() { 
            {



Spot it.

By Tweakers user YopY, Wednesday 12 January 2011 09:12

RobIII wrote on Tuesday 11 January 2011 @ 21:29:
[...]

:( Ga ergens anders lopen schelden :(

:+
:p heb al jaren niet meer op die site gekeken, enige opinie die mogelijk in mijn commentaar stonden zijn gebaseerd op helemaal niks en van-horen-zeggen.
Ch3cker wrote on Tuesday 11 January 2011 @ 21:40:

code:
1
2
3
// set up JMock expectations 
        context.checking(new Expectations() { 
            {



Spot it.
Haven't shat any bricks yet (read: don't see it, :()

By Tweakers user analog_, Thursday 13 January 2011 19:18

lal? hoe
hoe kan je het niet zien.


Comments are closed