如何在Spock集成测试中启动Spring Boot应用程序

42
什么是使用Spock运行集成测试(例如,@IntegrationTest)的最佳方法?我想要引导整个Spring Boot应用程序并执行一些HTTP调用来测试整个功能。
在这个工作中的JUnit测试中启动Spring Boot应用程序(首先启动应用程序,然后执行测试)。
@RunWith(SpringJUnit4ClassRunner.class)
@SpringApplicationConfiguration(classes = MyServer.class)
@WebAppConfiguration
@IntegrationTest
class MyTest {
   RestTemplate template = new TestRestTemplate();

   @Test
   public void testDataRoutingWebSocketToHttp() {
      def a = template.getForEntity("http://localhost:8080", String.class)
      println a
   }
}

但是使用Spock时应用程序无法启动(我该如何使其启动?):
@SpringApplicationConfiguration(classes = MyServer.class)
@WebAppConfiguration
@IntegrationTest
class MyTestSpec extends Specification {

   RestTemplate template = new TestRestTemplate();

   def "Do my test"() {
      setup:
      def a = template.getForEntity("http://localhost:8080", String.class)

      expect:
      println a
   }
}

当然,对于Spock,我在我的Gradle构建文件中指定了正确的依赖项。
...
dependencies {
   testCompile 'org.spockframework:spock-core:0.7-groovy-2.0'
   testCompile 'org.spockframework:spock-spring:0.7-groovy-2.0'
}
...

我相信可以毫无疑问地说,“使用Spock框架,应用程序无法启动”。如果能详细说明引发应用程序无法启动的具体细节将会有所帮助:是否有某些初始化为null?在测试运行结束时,是否没有打印出预期的日志(可能是由Spring框架记录)或输出信息? - cellepo
4个回答

52
问题在于Spock Spring正在寻找Spring的@ContextConfiguration注释,但无法找到它。严格来说,MyTestSpec已经被标注了@ContextConfiguration,因为它是@SpringApplicationConfiguration上的元注释,但Spock Spring不认为元注释是其搜索范围的一部分。目前有一个issue来解决这个限制。同时,您可以绕过此问题。 @SpringApplicationConfiguration所做的所有工作都是使用特定于Boot的上下文加载器自定义@ContextConfiguration。这意味着您可以通过使用适当配置的@ContextConfiguration注释来实现相同的效果:
@ContextConfiguration(loader = SpringApplicationContextLoader.class, classes = MyServer.class)
@WebAppConfiguration
@IntegrationTest
class MyTestSpec extends Specification {
    …
}

更新: 为了确保清楚(根据评论,似乎不是),要使此功能正常工作,您需要在类路径上拥有org.spockframework:spock-spring


有趣的是,我很确定以前尝试过这个,但无论如何,谢谢! - kuceram
在启动集成测试时,是否有标志可以提供VM?我已经复制了这个配置,但我的应用程序没有启动。我推断MyServer是具有Configuration和EnableAutoConfiguration注释的类,这样理解对吗? - Luis Muñiz
2
不需要任何标志,你对MyServer的推断是正确的。你可以在这里阅读更多关于@ContextConfiguration的信息:http://docs.spring.io/spring/docs/4.0.x/spring-framework-reference/htmlsingle/#integration-testing-annotations-spring。你的类路径上是否有spock-spring? - Andy Wilkinson
3
嗨,@AndyWilkinson,我回来告诉大家spock-spring在类路径中是必需的,然后看到了你的回复。谢谢,伙计! - Luis Muñiz
我已经更新了我的答案,使其更加清晰易懂。我还开了一个问题(https://github.com/spring-projects/spring-boot/issues/1234),以更新Spring Boot文档(http://docs.spring.io/spring-boot/docs/1.1.4.RELEASE/reference/htmlsingle/#boot-features-testing-spring-boot-applications-with-spock)中相关部分的内容。 - Andy Wilkinson
非常有用。谢谢。 - jeremyjjbrown

9
理想情况下,您将使用Spring Boot 1.4+和Spock 1.1+。Spring Boot添加了许多有用的注释。除了@ignacio.suay提到的@SpringBootTest之外,它们还添加了@TestConfiguration,如果您想在集成测试中使用Spring mocks而不是Mockito,则非常有用。如果您将@TestConfiguration与新的Spock DetachedMockFactory结合使用,则拥有将Spock Mocks注入Spring上下文所需的所有组件。我在这里发布了一篇带有示例代码的博客文章: Spring Integration Testing with Spock Mocks。简单来说,就是这样。
@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)
    }
  }
}

更新 现在有一个PR来获得更多的本地支持,以便将Spock Mocks注入Spring上下文进行集成测试。 新的@SpringBean@SpringSpy就像@MockBean@SpyBean注释一样

更新 Spock 1.2现在应该包括这些更改。 在文档更新之前,这里是一个Spock 1.2 Annotations for Spring Integration Testing的预览


TestConfiguration 可用于定义测试的其他 bean 或自定义项,模拟只是众多用例之一。您可以简单地使用 Spring 的 @MockBean 来使用 Mockito 进行模拟... - MariuszS
@MariuszS 这个问题涉及到Spock。在Spock中与Mockito一起使用会被认为是一种相当糟糕的代码气味。Spock已经内置了模拟功能。在回答这个问题时,@MockBean不支持Spock Mocks。 - Snekse

5
在新的Spring Boot版本(1.4)中,不再使用以下语句:
@SpringApplicationConfiguration(classes = MyServer.class)
@WebAppConfiguration
@IntegrationTest

您可以使用

标签。

@SpringBootTest(classes = MyServer.class)

您将能够启动应用程序上下文并设置任何依赖项。

如需更多信息,请查看此示例: http://ignaciosuay.com/how-to-do-integration-tests-with-spring-boot-and-spock/


1
这是一个启动引导应用程序然后运行Spock测试的设置:
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'
}
}

记得在 build.gradle 中添加 spock 和 groovy 的依赖。

dependencies {
    // other dependencies
    testCompile "org.codehaus.groovy:groovy-all:2.2.0"
    testCompile "org.spockframework:spock-core:0.7-groovy-2.0"
}

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接