Spring Integration Testing with Spock Mocks

How to inject Spock mocks into the Spring Context during integration tests..

Derek Eskens

We all know it’s important to write tests for our code. Often we write unit tests and mock out the dependencies for the class under test. But eventually there is something we need to test that we need a Spring Context spun up for with beans wired, security applied and properties injected. Even in those scenarios, however, there are times where we have a tricky or slow component that we would rather Mock than rely on a component. This post will cover how we can do that with Spock (or JUnit if that’s your thing).

Spring Integration Testing

Spring has lots of tools to help make integration testing easy and fast. Spring Boot makes things even easier. In Spring Boot 1.4 several improvements were introduced to reduce code, make your tests run faster and make edge cases easier to get around.

@MockBean

One of the nicest features introduced is the @MockBean annotation (as well as a @SpyBean). You can now quickly create Mockito mocks inside your test and have them injected into the Spring Context.

@RunWith(SpringRunner.class)
@SpringBootTest(webEnvironment = WebEnvironment.RANDOM_PORT)
public class SampleTestApplicationWebIntegrationTests {

    @Autowired private TestRestTemplate restTemplate;

    //You'll now be able to mock calls to VehicleDetailsService
    @MockBean private VehicleDetailsService vehicleDetailsService;
}

The problem is @MockBean is only supported using JUnit and Mockito. If you’re a fan of Spock, things are not quite as simple.

@TestConfiguration to the Rescue

As part of the Spring Boot 1.4 release, new @TestConfiguration and@TestComponent annotations were added to make it easy to define beans and configurations only intended for tests. This means we could create a mock and use it as a bean within the Spring context!

One caveat to note is how all of this impacts context caching.   Mocks are part of the cache key, so it would be best if you can define a single SpringIntegrationTestsConfiguration annotated with @TestConfiguration so all of your integration tests as a whole run faster.

Injecting Spock Mocks into the Spring Context

DetachedMockFactory

If you want to use Spock mocks, the first thing you’ll need to do is make sure you’re on Spock version 1.1 or higher.  This version introduced the ability to define mocks outside of a Spock Specification via the DetachedMockFactory. You’ll also need to include spock-spring if you don’t already have it.  While you’re bringing new stuff in, check out spock-subjects-collaborators-extension for your unit tests.

Now to put it all together. Once you have everything in place, all we need to do is define our mock as a bean in the TestConfiguration.

@TestConfiguration
class IntegrationTestMockingConfig {
    private DetachedMockFactory factory = new DetachedMockFactory()

    @Bean
    ExternalRankingService externalRankingService() {
        factory.Mock(ExternalRankingService)
    }
}

Once we have our mock bean defined, which will take the place of our normal primary bean, we can utilize it in our specs by just injecting the bean and using it like normal.

@SpringBootTest
@AutoConfigureMockMvc
@Import([IntegrationTestMockingConfig])
class PersonControllerIntTest extends Specification {

    @Autowired MockMvc mvc

    /**
     * This is our mock we created in our test config.
     * We inject it in so we can control it in our specs.
     */
    @Autowired ExternalRankingService externalRankingServiceMock

    def "GetRank"() {
        when: 'Calling getRank for a known seed data entity'
        MvcResult mvcResult = mvc.perform(get("/persons/1/rank"))
                                 .andExpect(status().is2xxSuccessful())
                                 .andReturn()

        then: 'we define the mock for JUST the external service'
        externalRankingServiceMock.getRank(_) >> {
            new Rank(level: 1, classification: 'Captain')
        }
        noExceptionThrown()

        when: 'inspecting the contents'
        def content = mvcResult.response.contentAsString

        then: 'result contains mix of mocked service data and actual wired component data'
        content == 'Capt James Kirk ~ Captain:Level 1'
    }
}

We now have full control over our mock service, so if you wanted to test how things behave when throwing exceptions or returning different values based on inputs, you can do that just as quickly and easily as if you were writing a unit test.

Wrap Up

EasyMock, Groovy Mocks, JMock, etc

The concepts above seem like they could easily be applied to other mocking techniques as long as you have the ability to define the mock outside of a test setup.  If you can define the mock inside a @TestConfiguration class, yet describe the behavior in your tests, then you should be in business.

Source Code

If you want to see how this works in more depth, the code is available on GitHub. The PersonControllerIntTest spins up a Spring context so we can make a MockMvc call to a REST endpoint which pulls data from an h2 database via a Spring Data repo, but the “Rank” data we would normally get from an external service has been mocked.

Test often and prosper!

Share this Post

Related Blog Posts

JVM

Camel is closing JPA sessions

March 9th, 2017

Apache Camel closes the JPA session after a step on the route. This has ideas on how to fix it.

Mike Hostetler
JVM

Implementing a Google DataFlow Pipeline

October 18th, 2016

Implementing a Google DataFlow Pipeline

Tim Drahn
JVM

Analyzing Kafka data streams with Spark

October 13th, 2016

An example Java 8 Spark application describes exactly once processing and analysis of a Kafka topic serving a (simulated) real time 911 call data stream.

Object Partners

About the author

Derek Eskens

Sr. Consultant

Derek has almost 15 years of client server experience in a broad array of industries. Before getting started in technology, Derek worked in Customer Service for many years. That experience has influenced how he approaches software; for new product development and legacy system rewrites alike, he’ll always be a user advocate striving to make things simplier.

Derek is also a rabid an avid foodie. If you need a restaurant recommendation while in Omaha, feel free to get in touch.