Grails 3.3 Integration Testing with Spock Mocks
It is easy to use the Spock Framework, shipped with Grails, to mock or stub a collaborator (such as a service) in our Grails unit tests.
The Testing chapter explains a bit about mocking collaborators, doWithSpring
/doWithConfig
callback methods, the FreshRuntime
annotation to mock beans in tests — but they’re mainly for unit testing.
How about mocking beans in an integration test?
Example
What if we have a controller
class AnimalRegistrationController { AnimalRegistrationService animalRegistrationService def arrival(ArrivalCommand arrival) { animalRegistrationService .registerArrival(arrival) .map { ArrivalErrorMessage aem -> renderErrors(aem) }.orElse { render status: 200 } } }
which calls a service, which calls a repository – which might do external calls which you don’t want to happen in an integration test.
class AnimalRegistrationService { ArrivalRepository arrivalRepository Optional registerArrival(Arrival arrival) { arrivalRepository.registerArrival(arrival) } }
Previously I wrote that Grails 3.3 has Spock 1.1 — which gave us a few new features to use such as a default answer for java.util.Optional
…but it gave us more!
1. DetachedMockFactory and TestConfiguration
Now we also have a DetachedMockFactory we can use to declare mocks outside the hierarchy of a outside of a Specification
, e.g. in a Spring configuration.
I got triggered by this article about Spring Integration testing, and I adjusted it to work for Grails 3.3 — which is based on Spring Boot but doesn’t quite use all the Spring annotations we’re used to in a vanilla Spring application.
So we create a configuration, specifically for testing, in src/test/groovy
using a DetachedMockFactory
like
import spock.mock.DetachedMockFactory import org.springframework.boot.test.context.TestConfiguration import org.springframework.context.annotation.Bean ... @TestConfiguration class TestRepositoryConfig { private DetachedMockFactory factory = new DetachedMockFactory() @Bean ArrivalRepository arrivalRepository() { factory.Mock(ArrivalRepository) } }
2. Integration test
We can now use the mocked bean in our Grails 3 integration test, by injecting it by type using @Autowired
. We can create the expectations as usual.
@Integration class ArrivalApiIntegrationSpec extends Specification { @Value('${local.server.port}') Integer serverPort @Autowired ArrivalRepository mockedArrivalRepository void "should create an arrival"() { given: 1 * mockedArrivalRepository.registerArrival(_) >> { Optional.empty() } when: def response = new RestBuilder().post('http://localhost:{serverPort}/api/arrivals') { urlVariables([serverPort: serverPort]) json { animalId = 1 date = '2017-01-01' } } then: response.status == 200 } }
3. Dependency
For the above to work, you actually have to pull in one essential spock-lang dependency.
Add it to your build.gradle
dependencies { ... testCompile 'org.spockframework:spock-core:1.1-groovy-2.4' testCompile 'org.spockframework:spock-spring:1.1-groovy-2.4'
Bada-bing. It’s done.
That’s it
We have now full control over our mocked beans, as if we were in a unit test.
Reference: | Grails 3.3 Integration Testing with Spock Mocks from our JCG partner Ted Vinke at the Ted Vinke’s Blog blog. |