Home > Spock Framework > What’s New In Spock 0.4? Episode 2: Better Mocking

What’s New In Spock 0.4? Episode 2: Better Mocking

As you may know, Spock comes with its own mocking framework, tightly integrated with the rest of the specification language. While certainly useful and already quite powerful, the mocking framework used to be one of the lesser developed parts of Spock, and had a few rough edges. Well, not anymore! Here are the improvements we’ve made for 0.4:

1. All mocks are thread-safe

Previously, things could go wrong if a mock was called from a thread other than the spec runner’s thread. Now, the mocking framework will behave correctly no matter how many threads you throw at it.

2. Vararg syntax

In Groovy, all methods whose last parameter has an array type can be called with vararg syntax (even methods implemented in Java). Therefore, it was only logical for Spock to support the same vararg syntax when defining interactions with such methods. Here is an example.

Suppose you have the following Java interface:

class Subscriber {
  public void receive(String... messages); // or: receive(String[] messages)
}

An interaction between a Publisher and its Subscriber could be described as follows:

given:
def publisher = new Publisher() // this is the object under test
def subscriber = Mock(Subscriber)
publisher.add(subscriber)

when:
publisher.sendNotifications() // let's say this should send "foo", "bar", and "baz"

then:
1 * subscriber.receive("foo", "bar", "baz") // vararg syntax

Alternatively, you could describe the interaction as follows:

then:
1 * subscriber.receive(["foo", "bar", "baz"]) // list syntax

Prior to 0.4, only the list syntax would have worked; the vararg syntax would have resulted in an InteractionNotSatisfiedError.

3. Sensible defaults for toString(), equals(), and hashCode()

Previously, a mock object’s toString(), equals(), and hashCode() methods didn’t get any special treatment. If you didn’t say otherwise, they would always return null, false, and 0, respectively. As of 0.4, more sensible defaults are in place: toString() now returns a descriptive message, equals() implements object identity, and hashCode() delegates to System.identityHashCode() (which behaves like Object.hashCode()).

Another change is that toString(), equals(), and hashCode() no longer match the wildcard method (as in “1 * foo._()”). This prevents tests from failing in the presence of tools like debuggers, which often call these methods for their own purposes.

Despite all this, toString(), equals(), and hashCode() can still be stubbed (and even mocked) like any other method, overriding their default behavior. Most Java mocking frameworks don’t support this feature.

4. _ as an abbreviation for _._

By default, Spock allows interactions that haven’t been specified explicitly, and returns default values (null, 0, false) for them. This helps to avoid over-specification and makes tests more resilient to change. In cases where you want to be more strict, add the following as your last interaction:

0 * _._ // no (other) call of any method on any mock object

In 0.4, this can be abbreviated to:

0 * _  // nothing else!
5. Support for property syntax

Properties and getter methods can now be stubbed with property syntax:

item.price >> 42

However, mocking a property setter still requires method syntax:

1 * item.setPrice(42) // not: 1 * (item.price = 42)
6. Ordered interactions

By default, interactions may occur in any order. For example:

when:
...

then:
1 * foo.moo()
2 * bar.baz()

Here we are expecting a total of three calls, but don’t demand a particular order. As a consequence, the following invocation order would be acceptable:

bar.baz()
foo.moo()
bar.baz()

In general this is a good thing, because it prevents you from specifying unimportant details which might change over time. However, sometimes order is really important. Therefore, you can now impose ordering constraints by using multiple then-blocks:

when:
...

then:
1 * tank.fill()
1 * door.close()

then:
1 * plane.takeOff()

To paraphrase: “I don’t care if you first fill the tank or close the cabin door, but you must do both before takeoff!” If this constraint isn’t met, Spock will throw a WrongInvocationOrderError.

That’s it for better mocking in Spock 0.4. In the next part of this series, we’ll have a look at how Spock 0.4 simplifies testing concurrent code.

About these ads
Categories: Spock Framework
  1. Stephan
    June 14, 2010 at 07:35

    This totally rocks – I find spock the best testing framework I know so far!

  2. June 14, 2010 at 08:33

    All these improvements are really very exciting! Well done!

  3. June 15, 2010 at 01:08

    Thanks guys. The really exciting stuff is still to come. :-)

  4. June 15, 2010 at 18:07

    I really like that similar to Mockito I only have to specify behaviour for the methods my test actually cares about. Can I use Hamcrest matchers as parameters to the mock method when verifying in the ‘then’ block?

    • June 15, 2010 at 18:23

      Instead of a Hamcrest matcher, you use a closure-based constraint:

      1 * foo.bar({ it > 5 })

      The one downside is that you won’t be able to tell from the failure message which value was passed (there’s certainly room for improvement here). To get around that, you can do:

      1 * foo.bar(_) >> { assert it[0] > 5 }

      This will give you the usual condition output.

    • June 15, 2010 at 18:27

      I should add that Spock supports other argument constraints besides the closure-based ones. Have a look here: http://code.google.com/p/spock/wiki/Interactions#Argument_constraints

  1. June 14, 2010 at 08:09

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s

Follow

Get every new post delivered to your Inbox.

%d bloggers like this: