使用Spock在控制器测试中模拟Spring服务

时间:2016-06-18 19:53:33

标签: spring-boot spock spring-test

我正在寻找一种模拟Controller中使用的Service bean的方法,因此我只能使用MockMvc测试控制器。但我找不到用Spock mock替换真正的bean的简单方法。一切都使用spring-boot 1.3.2版本。更多详情如下:

我有以下控制器类

@RestController
@RequestMapping(path = "/issues")
@AllArgsConstructor(onConstructor = @__(@Autowired))
public class NewsletterIssueController {

  private final GetLatestNewsletterIssueService latestNewsletterIssueService;

  @RequestMapping(
    method = RequestMethod.GET,
    path = "/latest"
  )
  public ResponseEntity getLatestIssue() {
    Optional<NewsletterIssueDto> latestIssue = latestNewsletterIssueService.getLatestIssue();

    if (latestIssue.isPresent()) {
        return ResponseEntity.ok(latestIssue.get());
    } else {
        return ResponseEntity.notFound().build();
    }
  }
}

此类的集成Spock测试:

@ContextConfiguration(classes = [Application], loader = SpringApplicationContextLoader)
@WebAppConfiguration
@ActiveProfiles("test")
class NewsletterIssueControllerIntegrationSpec extends Specification {

  MockMvc mockMvc

  @Autowired
  GetLatestNewsletterIssueService getLatestNewsletterIssueService

  @Autowired
  WebApplicationContext webApplicationContext

  def setup() {
    ConfigurableMockMvcBuilder mockMvcBuilder = MockMvcBuilders.webAppContextSetup(webApplicationContext)
    mockMvc = mockMvcBuilder.build()
  }

  def "Should get 404 when latest issue does not exist"() {
    given:
        getLatestNewsletterIssueService.getLatestIssue() >> Optional.empty() // this won't work because it is real bean, not a Mock
    expect:
        mockMvc.perform(MockMvcRequestBuilders
                .get("/issues/latest")
                .contentType(JVM_BLOGGERS_V1)
                .accept(JVM_BLOGGERS_V1)
        ).andExpect(MockMvcResultMatchers.status().isNotFound())
  }

}

我需要一种方法来将这个自动装配的bean替换为Mock / Stub,这样我就可以在给定的&#39;中定义交互。部分。

2 个答案:

答案 0 :(得分:1)

I'd create a local configuration in the test and override the bean there.

I don't know Groovy, but it would like this in Java:

@ContextConfiguration(classes = NewsletterIssueControllerIntegrationSpec.Conf.class, loader = SpringApplicationContextLoader.class)
@WebAppConfiguration
@ActiveProfiles("test")
class NewsletterIssueControllerIntegrationSpec extends Specification {
  @Configuration
  @Import(Application.class)
  public static class Conf {
    @Bean
    public GetLatestNewsletterIssueService getLatestNewsletterIssueService() {
      return mock(GetLatestNewsletterIssueService.class);
    }
  }

  // […]
}

Caveat: This approach works well with Mockito, but you might need a pre-release version of Spock for it to work, ref: https://github.com/spockframework/spock/pull/546

By the way: Spring Boot 1.4 will provide a @MockBean construction to simplify this.

答案 1 :(得分:0)

使用Spock 1.2,您可以使用SpringBean批注在Spring上下文https://objectpartners.com/2018/06/14/spock-1-2-annotations-for-spring-integration-testing/中注入模拟服务

因此,有了这个新注释,您的测试将是:

@WebMvcTest(controllers = [NewsletterIssueController], secure = false)
@AutoConfigureMockMvc
@ActiveProfiles("test")
class NewsletterIssueControllerIntegrationSpec extends Specification {

  @Autowired
  MockMvc mockMvc

  @SpringBean
  GetLatestNewsletterIssueService getLatestNewsletterIssueService


  def setup() {
      // nothing to do as SpringBean and WebMvcTest do the job for you
  }

  def "Should get 404 when latest issue does not exist"() {
    given:
        getLatestNewsletterIssueService.getLatestIssue() >> Optional.empty() // this won't work because it is real bean, not a Mock
    expect:
        mockMvc.perform(MockMvcRequestBuilders
                .get("/issues/latest")
                .contentType(JVM_BLOGGERS_V1)
                .accept(JVM_BLOGGERS_V1)
        ).andExpect(MockMvcResultMatchers.status().isNotFound())
  }

}