This page looks best with JavaScript enabled

Spock 1.2 - hassle-free Spring beans mocking in integration tests

 ·  ☕ 7 min read

Discover how to automatically inject Spock’s mocks and spies into the Spring context using Spock 1.2.

A lot of coffee beans with a partially filled cup laying on it. 3 beans in a cup have different color

Stubs/mocks/spies in Spock (and their life cycle) have been always tightly coupled with the Spock Specification class. It was only possible to create them in a test class. Therefore, using shared, predefined mocks (in both unit and integration tests) was problematic.

The situation was slightly improved in Spock 1.1, but only with the brand new Spock 1.2 (1.2-RC1 as a time of writing) using the Spock mocking subsystem in Spring-based integration tests is as easy as using @SpringMock for Mockito mocks in Spring Boot. Let’s check it up.

Btw, to be more cutting edge in addition to Spock 1.2-RC1, I will be using Spring Boot 2.1.0.M2, Spring 5.1.0.RC2 and Groovy 2.5.2 (but everything should work with the stable versions of Spring (Boot) and Groovy 2.4).

One more thing. For the sake of simplicity, in this article, I will be using a term ‘mock’ to refer also stubs and spies. They differs in behavior, however, in a scope of injecting it into the Spring context in the Spock tests it usually doesn’t matter.

Spock 1.1 - manual way

Thanks to the work of Leonard Brünings , mocks in Spock were decoupled from the Specification class. It was finally possible to create them outside and to attach it later on into a running test. It was the cornerstone of using Spock mocks in the Spring (or any other) context.

In this sample code we have the ShipDatabase class which uses OwnShipIndex and EnemyShipIndex (of course injected by a constructor :) ) to return aggregated information about all known ships matched by name.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
//@ContextConfiguration just for simplification, @(Test)Configuration is usually more convenient for Spring Boot tests
//Real beans can exist in the context or not
@ContextConfiguration(classes = [ShipDatabase, TestConfig/*, OwnShipIndex, EnemyShipIndex*/])
class ShipDatabase11ITSpec extends Specification {

    private static final String ENTERPRISE_D = "USS Enterprise (NCC-1701-D)"
    private static final String BORTAS_ENTERA = "IKS Bortas Entera"

    @Autowired
    private OwnShipIndex ownShipIndexMock

    @Autowired
    private EnemyShipIndex enemyShipIndexMock

    @Autowired
    private ShipDatabase shipDatabase

    def "should find ship in both indexes"() {
        given:
            ownShipIndexMock.findByName("Enter") >> [ENTERPRISE_D]
            enemyShipIndexMock.findByName("Enter") >> [BORTAS_ENTERA]
        when:
            List<String> foundShips = shipDatabase.findByName("Enter")
        then:
            foundShips == [ENTERPRISE_D, BORTAS_ENTERA]
    }

//    @TestConfiguration
    static class TestConfig {
        private DetachedMockFactory detachedMockFactory = new DetachedMockFactory()

        @Bean
        @Primary    //if needed, beware of consequences
        OwnShipIndex ownShipIndexStub() {
            return detachedMockFactory.Stub(OwnShipIndex)
        }

        @Bean
        @Primary    //if needed, beware of consequences
        EnemyShipIndex enemyShipIndexStub() {
            return detachedMockFactory.Stub(EnemyShipIndex)
        }
    }
}

The mocks are created in a separate class (outside the Specification) and therefore DetachedMockFactory has to be used (or alternatively SpockMockFactoryBean). Those mocks have to be attached (and detached) to the test instance (the Specification instance), but it is automatically handled by the spock-spring module (as of 1.1). For generic mocks created externally also MockUtil.attachMock() and mockUtil.detachMock() would need to be used to make it work.

As a result it was possible to create and use mocks in the Spring context, but it was not very convenient and it was not commonly used.

Spock 1.2 - first class support

Spring Boot 1.4 brought the new quality to integration testing with (Mockito’s) mocks. It leveraged the idea, originally presented in Springockito back in 2012 (when the Spring configuration was mostly written in XML :) ) to automatically inject mocks (or spies) into the Spring (Boot) context. The Spring Boot team extended the idea and thanks to having it as the internally supported feature it (usually) works reliably just by adding an annotation or two in your test.

Similar annotation-based mechanism is built-in in Spock 1.2 .

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
//@ContextConfiguration just for simplification, @(Test)Configuration is usually more convenient for Spring Boot tests
//Real beans can exist in the context or not
@ContextConfiguration(classes = [ShipDatabase/*, OwnShipIndex, EnemyShipIndex*/])
class ShipDatabaseITSpec extends Specification {

    private static final String ENTERPRISE_D = "USS Enterprise (NCC-1701-D)"
    private static final String BORTAS_ENTERA = "IKS Bortas Entera"

    @SpringBean
    private OwnShipIndex ownShipIndexMock = Stub()  //could be Mock() if needed

    @SpringBean
    private EnemyShipIndex enemyShipIndexMock = Stub()

    @Autowired
    private ShipDatabase shipDatabase

    def "should find ship in both indexes"() {
        given:
            ownShipIndexMock.findByName("Enter") >> [ENTERPRISE_D]
            enemyShipIndexMock.findByName("Enter") >> [BORTAS_ENTERA]
        when:
            List<String> foundShips = shipDatabase.findByName("Enter")
        then:
            foundShips == [ENTERPRISE_D, BORTAS_ENTERA]
    }
}

There is not much to be added. @SpringBean instructs Spock to inject a mock into a Spring context. Similarly, @SpringSpy wraps the real bean with a spy. In a case of @SpringBean it is required to initialize a field to let Spock know if we plan to use a stub or a mock.

In addition, there is also a more general annotation @StubBeans to replace all defined beans with stubs. However, I plan to cover it separately in an another blog post.

Limitations

For those of you who look forward to rewrite all Mockito’s mocks to Spock’s mocks in your Spock tests right after the lecture of this article there is a word of warning. Spock’s mocks - due to their nature and relation to Specification - have some limitations. The implementation under the hood creates a proxy which is injected into the Spring context which (potentially) replaces real beans (stubs/mocks) or wraps them (spies). That proxy is shared between all the tests in the particular test (specification) class. In fact, it also can span across other tests with the same bean/mock declarations in the situation Spring is able to cache the context (similar situation to Mockito’s mocks or Spring integration tests in general).

However, what is really important, a proxy is attached to a tests right before its execution and is detached right after it. Therefore, in fact, every test has it’s own mock instance (it cannot be applied to @Shared fields) and it is problematic for instance to group interactions from different tests and verify them together (which usually is quite sensible, but might lead to some duplication). Nevertheless, with using a setup block (or in-line stubbing) it is possible to share stubbing and interaction expectancy.

Summary

Spock 1.2 finally brings hassle-free Spock’s stubs/mocks/spies support for using them in the Spring context which is comparable with the one provided in Spring Boot for Mockito. It is just enough to add the spock-spring module to the project (runtime) dependencies. Despite some limitations, it is one point less for mixing native Spock’s mocking subsystem with external mocking frameworks (such as Mockito) in your Spock (integration) tests. And what is nice, it should work also in plain Spring Framework tests (not only Spring Boot tests). The same feature has been implemented for Guice (but I haven’t tested it).

Furthermore, Spock 1.2 brings also some other changes including better support for Java 9+ and it is worth to give it a try in your test suite (and of course report any potentially spotted regression bugs :) ).

One more good news. In addition to the Leonard’s work who made Spock 1.2 possible and a legion of bug reporters and PR contributors, since recently, there are also some other committers who are working on making Spock even better. Some of them you may know from some other popular FOSS projects. What is more, Spock 1.2 is (preliminary) planned to be the last version based on JUnit 4 and the next stable Spock version could be 2.0, leveraging JUnit 5 and (among others) its native ability to run tests in parallel.

The examples were written using Spock 1.2-RC1. It will be updated to 1.2-final once released. The source code is available from GitHub.

Btw, have you wonder if it is still worth using Spock in the time of JUnit 5? I try to help answer that question in my presentation which will be possible to see at JDD 2018 , this October in Kraków, Poland. See you there.

JDD 2018 logo with date

The lead photo based on the Couleur‘s work published in Pixabay, CC0 1.0
Share on

Marcin Zajączkowski
WRITTEN BY
Marcin Zajączkowski
Software Craftsman & Software Architect
An experienced architect aiming for high quality solutions. Very engaged in evangelising Software Craftsmanship, Clean Code and Test-Driven Development as a conference speaker and a trainer. A specialist in Continuous Delivery and Continuous Inspection of Code Quality. An enthusiast of Reactive Systems and broadly defined concurrency.

Besides, open source author and contributor, a proud Linux user.


Don't want to use the Utterance bot? Comments can be also placed directly on GitHub.
What's on this Page