The Spock Epic, Part 2: First Contact With Spock

Oleg Lunin
Universal Language
Published in
12 min readApr 21, 2016

--

Vulcan Salute

If you’ve heard the name and found out some basic info about Spock, you’re probably confused. There are a lot of specifics, lots of keywords and occasionally weird syntax. You take interest in those logical blocks of test code for the purpose of expressing stimuli and the reaction of your project’s features. Let’s perform a quick dive into what Spock has to offer from a developer’s point of view.

Building a Better Future

Spock uses certain Groovy-specific byte-code transformations, therefore, its tests should be processed by the Groovy compiler. For that, it is necessary to have Groovy on the classpath and have a Groovy-compiler plugin for your build system. For instance, in Gradle it’s really simple:

You have to configure and usually have a separate source folder (like src/test/groovy, which is the default for the Groovy compiler plugin) for your Groovy classes and your build tool has to have an extra step for compiling those classes. This step is present in Gradle by default after applying the Groovy plugin, so we won’t cover this with an example. All in all, that’s the setup necessary to delve into the features of Spock.

What Now, Captain?

Before we start writing specifications, let’s consider some task to be done. Since we are getting to know Spock, we might as well get to know his ship, the Enterprise, better. One of the many things essential to running a starship is managing its crew. Thus, our task is to write some officer managing specification for the Enterprise. In the name of honor!

Yeah, and Then What? Then What Happened?

One of the important parts of crew management is setting up clearance for accessing the ship’s systems. Let’s write some good ol’ Java code (with some Spock in it, of course), and, of course, use the test-first approach.

The Enterprise is docked and is in a lockdown state. All access is prohibited.

Then let’s write code to make it happen:

As you can see, the written code is mostly Java, even though the test class has a .groovy extension. The Specification superclass is the base class we have to utilize in order to have Spock. What’s new is the test blocks, which we also labeled to understand what’s being done within the test. As you can see, this is a logical scenario. We are given an Officer object, and what we expect is that when this object is fed into the TheEnterprise#clearanceToSystems method, then we obtain UNAUTHORIZED. One thing you might have noticed are the String method names. These allow for a very precise description of what is actually being tested. Basically, all the new Groovy and Spock things we introduced provide another degree for forming concise requirements by writing test code.

In order to expand the Enterprise’s functionality (and since it docked anyway), let us allow it to train new officers. After all, it is important to establish a legacy. All those officers will have some auto-generated names (they will make a name for themselves one day anyway, but now is not that time). And if no trainees (or a negative number of trainees, someone’s so smart) are required, then an empty list is returned. As an input we get a number of officers to train. As always, use a test-first approach (or a specification-first approach, to be more precise).

And of course we modify the Enterprise’s code:

The logic might seem complex at first, but there’s simply too much boilerplate in Java code. We’ll get to that in another article. Right now we have a simple if block, a list creation, and a bunch of new stuff.

First, the setup method represents the basic configuration of TheEnteprise object, which is sufficient for testing. We added a parameter that corresponds to the title of new trainees — who knows, maybe the captain would want to change those in future? Afterwards, we setup the Enterprise with this title, so that all trained officers get it. Another important thing is that when and then blocks could actually be added multiple times. In our case it’s convenient, since we only know that the system behaves in two different ways — either creates officers or returns an empty list.

You might have also noticed an odd thing. Why didn’t we have explicit asserts previously and then started inserting them all of a sudden, like this:

And this is something important to know about then blocks. All first-level boolean operations (i.e. those that are not externalized into a function invoked within a then block, or put inside a loop or another code block) are treated as assertions. As you can see, the check for officer’s name and title is within a loop, so we have to add an explicit assertion. In fact, let’s do a bit of refactoring.

We externalize this check into a separate method, and our then block starts to look like this:

Thus, one more thing to learn about then blocks is that other methods can be invoked within them. And, of course, in order for actual assertions to occur, we have to make them explicit.

You Dare Mock Me?

Actually, the Enterprise is about to take flight, so its systems are powering up, and we have got to stop restricting access to everyone. For that, we are going to introduce a special system (and lay off the test-first approach for a second, since it’s class architecture that’s at stake):

This system special service will be responsible for providing clearance to the Enterprise’s officers.

Since we test TheEnterprise class, we don’t require an implementation of the ClearanceProvider contract. We just know it will be there when we deploy the Enterprise. However, creating a special anonymous implementation for the purpose of every test could be tedious, especially since we only need to check whether certain methods are invoked and may need to check different behavior based on the scenarios to check. This highly implies the usage of a mock framework. Spock can do this too. Let’s change the test code to use mocks and add this code to TheEnterpriseSpec:

Naturally, our test changes as well:

This is something called a global interaction, i.e. global for all features in the given specification. If we were to define an interaction in the setup block (not setup() method), then such an interaction would also be global, but only in scope of a concrete feature. Don’t mind the weird symbols here, their explanation is coming up shortly.

Let’s not forget the implementation of TheEnterprise as well:

Now our ship fully uses the new service.

The Mock method provided by the Specification class creates a mock for the given object or contract. This article will not cover the Spy method, but Spock provides that one as well (though it does require access to CGLib for byte-code enrichment). We do occasionally require interaction testing (i.e. whether a certain dependency had its method invoked). And such an occasion is right now — our friend, Spock the Vulcan, requires unrestricted access to the ship’s systems without the need for access clearance scans. This is an urgent and logical request.

Of course, we are required to make a more precise requirement in the previous test:

This kind of syntax can seem confusing, but it’s actually pretty concise. What we’re saying is: one call (the number before the asterisk is the number of expected invocations) to the method testProvider#getClearance. We will accept any parameter (that’s the underline symbol, the wildcard), and will return LIMITED (the >>, right shift operator from Groovy, doesn’t perform any byte shifts, instead it indicates the return value). The number of invocations is optional; as we will see later, it could also be parameterized. Hence, we get an interaction and a verification in one package. Note that in the former case we didn’t specify a return type. Spock handles these things automatically — if the return type is not specified, it returns a default value for the given type (0 for numeric primitives, null for reference types etc). Such an approach is called lenient mocking.

By the way, this kind of interaction (defined within a then block) is called local, i.e. only valid for the given block. As we have already seen, in Spock the when and then blocks could be used multiple times within the same feature. In this way, local interaction make sense, as the interaction defined in one then block wouldn’t affect others. What should be noted, however, is that local interactions override global ones in case of overlap. In our case, the setup() method has an interaction that defines the behavior (i.e. return type) of testProvider#getClearance(); whereas the local blocks verify the interaction of the same method. Hence, you will not be able to specify global behaviour and then verify only interactions in local blocks. So when we write

inside a then block, Spock will treat it as another interaction, and since we didn’t specify the return value, it will be different from the one defined in setup() method. This has been the folly of many a developer, so it is very important to understand. Also take note that we have specified the input parameter to match, hence if we change Spock’s name to something else, the test will fail.

We clean up the setup() method:

And now we modify the code of TheEnterprise in order to fit these changes:

As a result, all tests pass.

An Exception to The Prime Directive

Another important part of testing is testing for exceptions. Leonard McCoy is a very impatient man and a very illogical one. So once Spock got access to all starship systems, he set up a special greeting. Let’s code it up:

Let’s also add code to support this to TheEnterprise:

This time we used the setup block instead of a given block, which makes no difference, except for semantic one (i.e. given could be associated with input data, whereas setup could be associated with input data and prior conditions). What’s important is the thrown() method, which does an automatic cast to our desired return type (AssertionError). The thrown() method is a check itself, if you don’t want to check the exception message.

Share the pain

Spock has its own methods for setting up and tearing down tests. A developer who started using Spock won’t get confused, since it has a lot of peculiarities anyway. So, instead of @Before and @After Spock uses setup() and cleanup(), and for class-level setup it uses setupSpec() and cleanupSpec() instead of @BeforeClass and @AfterClass. Actually, Spock has its own terminology for these things — these methods are called fixture methods; whereas other methods (the actual tests) are called feature methods, which is quite logical, since a system’s features are presented through blocks of stimuli and reactions. One thing that stands out is the @Shared annotation. Instead of using static fields, @Shared objects provide a common object to be utilized by all features. It could be a heavy object, whose creation takes a lot of time. Or an object, which has the same interaction necessary for all features. Either way, it should be configured in setupSpec() or initialized right away. For the sake of the example, we will do initialization in the setupSpec() block.

We might want to reuse officers defined in our logical blocks, so it is a good idea to share them between tests. Remember that Spock creates a new test class instance for every feature, hence sharing allows… well, sharing values between those test class instances.

Afterwards all the given and setup blocks we had remain kind of useless, but we will leave them be for visibility’s sake, like this:

Thus we made certain test objects reusable with the help of @Shared annotations. What about static variables, you might ask? Well, they are also an option! However, according to Spock’s documentation: “Static fields should only be used for constants. Otherwise shared fields are preferable, because their semantics with respect to sharing are more well-defined.” In our case we have already declared one static variable in our test and thus followed this convention to the letter.

Sunday Data Driver

A lot of requirements could be merged without increasing complexity. Whole sets of data could be processed with the same rules. Similarly, tests with duplicated logic could be merged into a single test and given parameters for inputs and output. JUnit has evolved its support for test parametrization, but it still doesn’t cover all the cases that Spock does, nor does it provide the same convenience.

For our example it would be wise to check multiple outputs of TheEnterprise#clearanceToSystems() in order to enforce the correctness of its implementation. Thus we consider different inputs and write the following test:

We have instructed testProvider#getClearance() to return whatever testClearanceBehavior() method returns.

As you can see, we are using a where block to supplement the parameters in other blocks. The variables name, title, and expectedResult are never declared. Instead, they are presented in a table-like structure (don’t worry, any modern IDE should help with formatting these). All setup parameters are delimited by a single pipe character, whereas the expected parameters are delimited by a double-pipe string. Again, this is done to enforce the visibility of parameterized data. With such a table-like structure and demarcation convention it is very easy to develop tests and understand the developed code later.

Another hefty addition is the @Unroll annotation, which tells Spock to treat every row of data in the where clause as a separate test case. Thus in our test report for this test we will see 13 test cases. And what’s more — @Unroll enables including parameters from our data table into every separate test case’s name (this is done by including parameter names along with a # character) — checkout the test name, then checkout how the IDE displays it:

Your Framework Will be Assimilated — Resistance is Futile

Spock is primarily a unit-testing framework. However, efforts have been made in order to integrate it with Spring framework. No additional runners are required, like in JUnit, only a spock-spring library in your classpath. Afterwards, the standard spring-test configuration could be used; and the Spock block syntax will still be available. And, of course, through Spring other frameworks are supported (like Hibernate). DBUnit tests could be written as well without any special configuration.

What effort is needed on your part to use Spock with your Spring integration tests? In Gradle it’s:

Hence it is only a matter of adding a jar to your classpath. Afterwards you can write tests that include Spring Context(s) in Spock fashion. The required dependencies are spring-beans and spring-test (for test compilation), and also the aforementioned Spock-spring. We also included spring-context for convenience. Let’s add a configuration:

Afterwards we add a test that uses this context and runs our parameterized test on the bean from context:

Sorry, we cheated a little here, using Groovy to set a field via reflection. We didn’t want to write a setter in TheEnterprise class, which would be specifically used for tests. Do you want to? Anyway, here is the test, practically as it was:

Yes, it’s that simple. We could insert Spock mocks into beans from Spring context in order to avoid any useless interactions (like mocking DAOs). We can also see that explicit setters are not necessary for Groovy (it’s not a Spock feature).

Spock’s documentation also points to integration with other frameworks, like Guice, Tapestry and Grails, hence there are even more ways to add Spock support to integration tests.

Ignorance is Bliss

Spock offers advanced control over ignoring your tests. First, we add a simple @Ignore annotation on those tests that became useless after we added the parameterized test:

Please note that it’s spock.lang.Ignore, not org.junit.Ignore.

When The Enterprise takes flight, it would be quite impossible to train new officers in outer space. Therefore, we have to have some kind of conditional ignore of features. Thankfully, Spock provides a convenient way to ignore tests based on environment configuration. Thus we add a system property:

And then add an @IgnoreIf statement to the relevant test.

And this test gets ignored, since we set the property to true. Spock also provides pre-set property sources, such as jvm for the properties of your JVM, os for the properties of your OS, env for the environment properties you run tests in etc.

Other test run control options are available (@Timeout for failing tests that execute too slowly, @IgnoreRest to ignore a subset of tests, and lots more), but won’t be in scope of this article. However, we’re sure you’ll be curious to know about the possibilities.

Nobody is Perfect

Of course, Spock is not perfect. Once it is used, once the Specification class is extended, there is no turning back. Test code could not be written in another fashion. Thus certain requirements could not be adequately presented. For example, the DBUnit test framework integrates with Spring, providing a convenient way for testing input and output of code that involves the database layer. Consider this test code (which has absolutely no connection to our sample code and just demonstrates DBUnit-Spring-Spock combo):

Only database states are verified, nothing else should be checked; however, Spock forces the usage of a given block (a when block could not be used without a then block), making the whole test code confusing. And there is no option to avoid using Spock blocks for some of the tests.

Furthermore, byte-code modification is prone to errors, and in some circumstances related compilation errors will appear without any reason. Here is an issue example, where byte-code modification occurred unexpectedly: https://code.google.com/p/spock/issues/detail?id=388&colspec=ID%20Type%20Module%20Status%20Milestone%20Reporter%20Owner%20Summary&start=100. Byte-code modification errors are not commonplace, but having one on your hands is not something pleasant.

What to Do Next?

Well, if you are not convinced to check out Spock right away, go look at the documentation: https://github.com/spockframework/spock. All the fine things are available — community support, samples, best practices and even a web console. And, of course, additional cool features of Spock.

Notes

The relevant code snippets were written in IntelliJ Idea IDE; the final version of the project is available on GitHub here: https://github.com/jiallombardo/spock_basics.

--

--