What is the best way to run an integration test (e.g., @IntegrationTest) with Spock? I would like to bootstrap the whole Spring Boot application and execute some HTTP calls
Ideally you'll use Spring Boot 1.4+ and Spock 1.1+.
Spring Boot added a lot of useful annotations. In addition to that @SpringBootTest
that @ignacio.suay mentioned, they also added @TestConfiguration
which is useful if you want to use Spring mocks in your integration tests instead of Mockito.
If you combine @TestConfiguration
with the new Spock DetachedMockFactory
, then you have all of the components you'll need to inject Spock Mocks into your Spring context.
I have a blog post with sample code here: Spring Integration Testing with Spock Mocks.
The quick and dirty is this
@SpringBootTest
class MyIntegrationTest extends Specification {
@Autowired ExternalRankingService externalRankingServiceMock
def "GetRank"() {
when:
classUnderTest.getRankFor('Bob')
then:
1 * externalRankingServiceMock.fetchRank('Bob') >> 5
}
@TestConfiguration
static class Config {
private DetachedMockFactory factory = new DetachedMockFactory()
@Bean
ExternalRankingService externalRankingService() {
factory.Mock(ExternalRankingService)
}
}
}
UPDATE
There is a PR to get more native support in Spock for injecting Spock Mocks into the Spring context for integration testing. The new @SpringBean
and @SpringSpy
would be like the @MockBean
and @SpyBean
annotations
UPDATE Spock 1.2 should now include these changes. Until the documentation is updated, here is a preview of the Spock 1.2 Annotations for Spring Integration Testing .
The problem is that Spock Spring is looking for Spring's @ContextConfiguration
annotation and doesn't manage to find it. Strictly speaking MyTestSpec
is annotated with @ContextConfiguration
as it's a meta-annotation on @SpringApplicationConfiguration
but Spock Spring doesn't consider meta-annotations as part of its search. There's an issue to address this limitation. In the meantime you can work around it.
All that @SpringApplicationConfiguration
is doing is customising @ContextConfiguration
with a Boot-specific context loader. This means that you can achieve the same effect by using an appropriately configured @ContextConfiguration
annotation instead:
@ContextConfiguration(loader = SpringApplicationContextLoader.class, classes = MyServer.class)
@WebAppConfiguration
@IntegrationTest
class MyTestSpec extends Specification {
…
}
Update: Just to make sure it's clear (and based on the comments, it wasn't), for this to work you need to have org.spockframework:spock-spring
on the classpath.
In the new Spring Boot version (1.4) instead of using:
@SpringApplicationConfiguration(classes = MyServer.class)
@WebAppConfiguration
@IntegrationTest
You could use
@SpringBootTest(classes = MyServer.class)
and you will be able to start the application context and set any dependency.
for further information, please have a look to this example: http://ignaciosuay.com/how-to-do-integration-tests-with-spring-boot-and-spock/
Here is a setup which starts up boot applicaiton and then runs spock tests:
class HelloControllerSpec extends Specification {
@Shared
@AutoCleanup
ConfigurableApplicationContext context
void setupSpec() {
Future future = Executors
.newSingleThreadExecutor().submit(
new Callable() {
@Override
public ConfigurableApplicationContext call() throws Exception {
return (ConfigurableApplicationContext) SpringApplication
.run(Application.class)
}
})
context = future.get(60, TimeUnit.SECONDS)
}
void "should return pong from /ping"() {
when:
ResponseEntity entity = new RestTemplate().getForEntity("http://localhost:8080/ping", String.class)
then:
entity.statusCode == HttpStatus.OK
entity.body == 'pong'
}
}
And remember to add dependencies to spock and groovy inside build.gradle
dependencies {
// other dependencies
testCompile "org.codehaus.groovy:groovy-all:2.2.0"
testCompile "org.spockframework:spock-core:0.7-groovy-2.0"
}