When we write tests or specifications using Spock for our Spring Boot application, we might want to replace some Spring components with a stub or mock version. With the stub or mock version we can write expected outcomes and behaviour in our specifications. Since Spock 1.2 and the Spock Spring extension we can use the @SpringBean
annotation to replace a Spring component with a stub or mock version. (This is quite similar as the @MockBean
for Mockito mocks that is supported by Spring Boot). We only have to declare a variable in our specification of the type of the Spring component we want to replace. We directly use the Stub()
or Mock()
methods to create the stub or mock version when we define the variable. From now on we can describe expected output values or behaviour just like any Spock stub or mock implementation.
To use the @SpringBean
annotation we must add a dependency on spock-spring
module to our build system. For example if we use Gradle we use the following configuration:
... dependencies { ... testImplementation("org.spockframework:spock-spring:1.3-groovy-2.5") ... } ...
Let's write a very simple Spring Boot application and use the @SpringBean
annotation to create a stubbed component. First we write a simple interface with a method that accepts an argument of type String
and return a new String
value:
package mrhaki.spock; public interface MessageComponent { String hello(final String name); }
Next we use this interface in a Spring REST controller where we use constructor dependency injection to inject the correct implementation of the MessageComponent
interface into the controller:
package mrhaki.spock; import org.springframework.http.MediaType; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.RequestParam; import org.springframework.web.bind.annotation.RestController; @RestController public class MessageController { private final MessageComponent messageComponent; public MessageController(final MessageComponent messageComponent) { this.messageComponent = messageComponent; } @GetMapping(path = "/message", produces = MediaType.TEXT_PLAIN_VALUE) public String message(@RequestParam final String name) { return messageComponent.hello(name); } }
To test the controller we write a new Spock specification. We use Spring's MockMvc support to test our controller, but the most important part in the specification is the declaration of the variable messageComponent
with the annotation @SpringBean
. Inside the method where we invoke /message?name=mrhaki
we use the stub to declare our expected output:
package mrhaki.spock import org.spockframework.spring.SpringBean import org.springframework.beans.factory.annotation.Autowired import org.springframework.boot.test.autoconfigure.web.servlet.WebMvcTest import org.springframework.test.web.servlet.MockMvc import spock.lang.Specification import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get @WebMvcTest(MessageController) class MessageControllerSpec extends Specification { @Autowired private MockMvc mockMvc /** * Instead of the real MessageComponent implementation * in the application context we want to use our own * Spock Stub implementation to have control over the * output of the message method. */ @SpringBean private MessageComponent messageComponent = Stub() void "GET /message?name=mrhaki should return result of MessageComponent using mrhaki as argument"() { given: 'Stub returns value if invoked with argument "mrhaki"' messageComponent.hello("mrhaki") >> "Hi mrhaki" when: 'Get response for /message?name=mrhaki' final response = mockMvc.perform(get("/message").param("name", "mrhaki")) .andReturn() .getResponse() then: response.contentAsString == "Hi mrhaki" } }
Written with Spock 1.3-groovy-2.5 and Spring Boot 2.1.8.RELEASE.