IntelliJ IDEA
IntelliJ IDEA – the Leading Java and Kotlin IDE, by JetBrains
Tutorial: Spock Part 4 – Mocking and Stubbing
In Part 4 of our Spock tutorial, we look at mocking and stubbing. The Spock framework has mocking and stubbing built in. Creating, using and verifying mocks and stubs is nicely aligned with the way the tests are written, and can lead to very readable tests.
Tutorial: Spock
- Part 1 – Getting Started
- Part 2 – Writing Tests
- Part 3 – Data Driven Testing
- Part 4 – Mocking and Stubbing
- Part 5 – Other Useful Tips
These blog posts cover the same material as the video. This provides an easy way for people to skim the content quickly if they prefer reading to watching, and to give the reader/watcher code samples and links to additional information.
Mocks
Sometimes we need to mock out classes or APIs to assert the expected behaviour. Mocking is built in to Spock, we don’t need a separate library or framework for Mock support. It’s also possible to mock concrete classes. If you’re used to other Mocking frameworks you might expect to only be able to mock Java interfaces, but Spock lets us easily create a mock from a concrete class.
The given
block of a Spock test is the perfect place to set up mocks for our test. It’s clear then that this is all code that’s required to run the test, but is not the code that’s being tested itself.
def "should be able to mock a concrete class"() {
given:
Renderer renderer = Mock()
def polygon = new Polygon(4, renderer)
when:
polygon.draw()
then:
4 * renderer.drawLine()
}
This test mocks a Renderer class, which is a concrete Java class. We can do this either by declaring a variable with type Renderer, and calling Mock without any arguments:
Renderer renderer = Mock()
…or if we prefer to use Groovy’s def
to define our variables, we’ll need to pass the type in as an argument to the Mock method:
def renderer = Mock(Renderer)
Bear in mind that if you declare it using “def”, this variable is using Groovy’s dynamic typing, and so isn’t strongly recognised as a Renderer type by the IDE or by the code. This is fine if you’re not doing much with the mock, but you might sometimes want to specify the type more clearly, this will certainly be more natural for Java developers.
The given
block also sets up a Polygon with the given renderer, calling the constructor with the numberOfSides
and the mocked renderer
.
The when
section defines the call that’s actually the thing we’re testing, in this test we want to see what happens when we call the draw
method on this polygon. Make sure there’s a draw
method on the polygon, at this stage it can be empty because we’re doing a bit of TDD:
public void draw() {
}
The then
block defines the expectations. Spock has a nice, clear syntax for defining the behaviour we expect to see on the mock. In this test, we might expect to see four calls on the renderer’s drawLine
method, given that the polygon has four sides. The then
block states we expect to see renderer.drawLine
called 4 times.
Run this test now, it should fail. This is because the methods don’t do anything yet. We expected to see this drawLine method called four times, but it wasn’t called at all.
Go into the implementation of the Polygon.draw
method and change it to call the renderer’s drawLine method in here as many times as there are sides (note that this is an extremely over-simplified example to demonstrate the testing):
public void draw() {
for (int i = 0; i < numberOfSides; i++) {
renderer.drawLine();
}
}
Re-run the test, it should pass. The code is calling drawLine
on the renderer
mock four times.
Mocks are a powerful and useful tool to make sure that the code that we’re testing is calling the APIs that we expect, in the way we expect.
Stubs
Mocks are useful for checking calls out of our code, Stubs are useful for providing data or values into the code we’re testing.
Let’s see an example of a stub in a new test method.
def "should be able to create a stub"() {
given:
Palette palette = Stub()
palette.getPrimaryColour() >> Colour.Red
def renderer = new Renderer(palette)
expect:
renderer.getForegroundColour() == Colour.Red
}
The given
block sets up the preconditions for the test. This time, we’re going to use the Stub()
method to create a Stub of the concrete Palette class. Like with Mock()
, you can define it this way, or use def
and pass the type into the Stub()
method.
Next the test sets up the palette
stub with the values it will produce when called by our code. We use right-shift (>>
) to state that when the method getPrimaryColour
is called, the Enum value Red
will be returned.
The last step of setup is to create the Renderer with this stub palette
. If you’re following along with this code in the IDE, make sure your Renderer looks something like:
public class Renderer {
private Palette palette;
public Renderer(Palette palette) {
this.palette = palette;
}
public void drawLine() {
}
}
The test uses an expect
label because the test and the assertion are combined – we expect that when we call getForegroundColour
, this will return Colour.Red
. This test states that we expect getForegroundColour
to return the same colour as the palette’s primary colour.
Once again, we can use test-driven development here – we can use the test to drive out what we expect the methods to look like even if they don’t exist yet. Use ⌥⏎ (macOS), or Alt+Enter (Windows/Linux), on any red method names to get IntelliJ IDEA to create the most basic methods that makes the code compile, then run the test.
It should fail if we haven’t implemented the details for getForegroundColour
. It’s good to see the test fail first, it often indicates the test is checking the right thing, even if that right thing hasn’t been implemented yet.
Change the getForegroundColour
method to return the palette’s primary colour.
public Colour getForegroundColour() {
return palette.getPrimaryColour();
}
Re-run the test, it should pass. The test injects a Stub palette
into the renderer
, we tell the stub palette
what to return when the getPrimaryColour
method is called, so we can check that the renderer
does what it’s supposed to do when we call getForegroundColour
.
If we had set this up as a Mock instead of a Stub, this would have worked as well. Mock objects support the mocking behaviour we saw in the previous test and the stubbing behaviour we saw here, whereas Stub objects only support stubbing, and not mocking. My preference is to keep stub and mock behaviour separate where possible, so it’s usually best to use Stubs just for stubbing and Mocks only for mocking.
Conclusion
In this blog, we looked at mocking and stubbing. Now you know how to:
- Create a mock and write a test that shows a particular method was called when the test was run
- Create a stub to provide an expected value, so a test can verify that expected value is used
Spock has much more to offer than this, stay tuned for further blog posts, watch the full video, or take a look at the excellent reference documentation.