IntelliJ IDEA
IntelliJ IDEA – the Leading Java and Kotlin IDE, by JetBrains
Tutorial: Spock Part 2 – Writing Tests
In Part 2 of our Spock tutorial, we’ll look at writing Spock tests in more detail, including some tips on how to get the most from Groovy.
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.
Given, When and Then
One of the most powerful features of Spock is how descriptive it is. It’s not just for testing code, it’s for describing and documenting expected behaviour.
In our last test, we used the expect
label to indicate a simple expectation. Spock supports a number of different labels for test blocks. My personal favourite set of labels is given
, when
, then
. Let’s create a new test method:
def "should demonstrate given-when-then"() {
given:
def polygon = new Polygon(4)
when:
int sides = polygon.numberOfSides
then:
sides == 4
}
You’ll also need to create a Java Polygon class in src/main/java
. We can use Spock to test Java or Groovy code.
public class Polygon {
private final int numberOfSides;
public Polygon(int numberOfSides) {
this.numberOfSides = numberOfSides;
}
}
Back in the test, you can see the given
label is used to indicate that this part of the method is setting up the required conditions. This test needs an instance of Polygon
with four sides.
given:
def polygon = new Polygon(4)
Next, we say when
. This is the thing we’re actually testing, the behaviour we’re trying to check. This test makes sure the numberOfSides
has been correctly set and that we can get it from the class.
when:
int sides = polygon.numberOfSides
Finally, we say then
. This section checks all the conditions we expect to have been met. This test checks that the number of sides is four, the original number of sides passed in to the constructor.
then:
sides == 4
You can read a given/when/then test like this:
- given that I have a Polygon with four sides,
- when we get the number of sides
- then that number of sides should be equal to four
Run this test to see if it passes. You should get an output like:
Using these labels might look like it’s doubling the number of lines of code for a test method, but remember that tests often have many more lines of code in them, and grouping them in this way helps describe what’s happening.
Groovy Tips for Java Developers
Before going further with Spock examples, let’s take a look at some things which might look odd if we’re used to using Java to test Java code.
when:
int sides = polygon.numberOfSides
In the when
section of the test in the last example, we’re actually reaching right inside the Java class to inspect the value of a private field. We can do this from Groovy, which can be helpful for testing private fields or methods, without compromising their visibility in production code. However, IntelliJ IDEA will give us a warning in case this is not something we want to do intentionally.
Often, if we need something visible for testing we probably will need it visible in production code too.
Go into your Polygon class and press ⌥⏎ (macOS), or Alt+Enter (Windows/Linux) on the field and get IntelliJ IDEA to create a getter for the field.
Look at the test now you’ve made this change to the Polygon class. Now there’s something odd about the polygon.numberOfSides
call. The warning has gone, and numberOfSides
is no longer in bold, it no longer looks like it’s referencing a field. In fact, it’s not. If we hold down ⌘, or Ctrl, and move our mouse over this, we can see it’s actually referencing the method getNumberOfSides
.
If we’re calling a Java getter from Groovy code, we can miss out the “get” at the start of the method name, and Groovy will still use the getter rather than the field. This can be useful to reduce noise, but you can still use the full method name with “get” if you prefer, it depends upon what you think is most readable. Sometimes removing the “get” might be confusing.
Label Flexibility
We’ve seen expect
and we’ve seen given, when and then
, but Spock provides a lot of flexibility around which labels to use and when. It’s all about creating descriptive tests.
In our last test, it did look like the labels might add a lot of noise for such a short test. The setup, for example, is very simple, and we could inline this into the actual test itself. If you inline the setup code into the when
block, you can remove the given
label and just use when and then.
def "should demonstrate given-when-then"() {
when:
int sides = new Polygon(4).numberOfSides
then:
sides == 4
}
The test should run successfully like before. We have the flexibility we need to label blocks of code in whichever way we think is most readable, so the tests are documenting the expected behaviour of the code.
Spock Plugin
We’ve seen that we can write, run and refactor Spock tests in IntelliJ IDEA. We can also install a plugin for extra support, if we want (note: this is written and supported by a third party, it’s not a JetBrains plugin). It adds some helpful behaviour, like syntax highlighting for the labels, and inspections to help us see if we’re using the labels correctly.
We need to go to the plugin settings and search the plugin marketplace for Spock plugins.
Using this plugin is entirely optional, IntelliJ IDEA provides all the support you need to write and run Spock tests.
Expecting Exceptions
Testing isn’t just about testing the happy paths. We should also check the exceptional cases.
Often, we want to check that the right sort of Exception gets thrown. If you create a new polygon with zero sides, this sounds like something that should cause an error. Let’s write a test:
def "should expect Exceptions"() {
when:
new Polygon(0)
then:
thrown(TooFewSidesException)
}
The then
block checks that the correct Exception has been thrown. We do this by calling thrown()
with the class of the Exception that we expect to be thrown. In our case, we expect a TooFewSidesException to have been thrown by the call to the constructor (create this Exception in your project if you want this test to run correctly).
Note that in Java, we’d normally have to add .class to the end of TooFewSidesException
to define this is the type of exception. In Groovy we don’t need this, but you can add it if you think it makes the code clearer:
then:
thrown(TooFewSidesException.class)
Run this test to see if the behaviour of the code is correct. The test should fail because the Polygon class doesn’t throw this Exception yet.
Change the constructor of Polygon so that it checks for the number of sides and throws the Exception if there are fewer than three sides:
public Polygon(int numberOfSides) {
if (numberOfSides <= 2) {
throw new TooFewSidesException("You can't have fewer than 3 sides for a polygon", numberOfSides);
}
this.numberOfSides = numberOfSides;
}
Re-run the test, it should pass.
We can even assign the thrown Exception to a variable so that we can perform some more checks on it. Let’s check the number of sides stored on the TooFewSidesException to see if it matches the number of sides the Polygon was created with.
then:
def exception = thrown(TooFewSidesException)
exception.numberOfSides == 0
Rerun the test: try using Find Action ⌘⇧A (macOS), or Ctrl+Shift+A (Windows/Linux) and typing “Rerun”, to rerun the test. The test should pass.
Conclusion
In this blog, we covered some tips on writing tests using Spock and Groovy. Now you know how to:
- Use labels to define the blocks of your tests
- Read Groovy code that may behave differently to how a Java developer would expect
- Install the Spock plugin to get extra information on the tests
- Write tests that expect exceptional cases
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.