So Groovy MockFor puzzled me for a while. Lets look at an example of testing a very simple Service class that is required to process entries in a map and store each of them:
And the Datastore looks like this:
Now lets say we want to test that when the Service is initialised that we "open" the Datastore. This can be achieved with MockFor quite easily:
Lets take see what is going on here.
- On line 9 we create a new MockFor object to mock the DataStore.
- On line 10 we demand that the open method is called and we return true.
- On line 11 we get a proxy instance that we can pass into the Service class.
- On line 14 we create an instance of the Service class passing in our mocked Datastore.
- Finally on line 17 we verify that all our demands have been met.
junit.framework.AssertionFailedError: verify[0]: expected 1..1 call(s) to 'open' but was called 0 time(s)
Brilliant. Now lets move onto a move complicated example. Lets say we want to test the processEntry method takes each of the entries and stores them in the Datastore. This is when it became apparent to me that what is happening on line 10 is a closure that is executed when the mocked method is called. It just happened to return true as that was the last statement in the closure and Groovy doesn't always require the return statement. My first attempt to test the above scenario led me to:
Okay so the test fails now:
junit.framework.AssertionFailedError: verify[1]: expected 1..1 call(s) to 'storeField' but was called 0 time(s)
However we can make it pass without writing any meaningful code:
But changing line 6 to the following:
Means we'll get the following failure:
Assertion failed:
assert v == "someValue"
| |
| false
this is a made up value
And then we can actually write some sensible code to make it pass as now we're actually asserting that the correct values have been passed in.
Now finally onto the problem from the title. How do we mock multiple invocations to the same method with different arguments? We may want to do this when we verify all the values in the map passed to processEntry are passed to the Datastore rather than just the first one. This is where if you are coming from a Java/Mockito background you need to think differently. The solution I used in a similar situation looked like this:
Here we've written more complicated code in the closure that will be executed when the storeField method is called. Depending on what the key is that has been passed in a different assertion is executed. Of course you need to add some more code to the closure if we wanted to verify that no other values have been passed in.
This same style can be used to return different values depending on input parameters e.g.
I've also included the equivalent Java Mockito code for comparison.
1 comment:
Hi Chris, I've used something similar to return different results for different invocations. I used an array where each entry is a map of parameters to assert against and a value to return. I then just have some code in the demand closure that pops an entry from the array, separates the params from the return value, does the param assertions and returns the return value.
Post a Comment