Интеграционный тест с Spring Boot и Spock

#java #groovy #gradle #spring-boot #spock

#java #groovy #gradle #spring-boot #spock

Вопрос:

Каков наилучший способ запуска интеграционного теста (например, @IntegrationTest ) с помощью Spock? Я хотел бы загрузить все приложение Spring Boot и выполнить несколько HTTP-вызовов, чтобы протестировать всю функциональность.

Я могу сделать это с помощью JUnit (сначала запускается приложение, а затем выполняются тесты):

 @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'
}
...
  

Я что-то упускаю?

Ответ №1:

Проблема в том, что Spock Spring ищет @ContextConfiguration аннотацию Spring и не может ее найти. Строго говоря MyTestSpec , аннотируется @ContextConfiguration , поскольку это мета-аннотация @SpringApplicationConfiguration , но Spock Spring не рассматривает мета-аннотации как часть своего поиска. Существует проблема, связанная с этим ограничением. Тем временем вы можете обойти это.

Все, что @SpringApplicationConfiguration делается, — это настройка @ContextConfiguration с помощью загрузчика контекста, зависящего от загрузки. Это означает, что вы можете добиться того же эффекта, используя вместо этого соответствующим образом настроенную @ContextConfiguration аннотацию:

 @ContextConfiguration(loader = SpringApplicationContextLoader.class, classes = MyServer.class)
@WebAppConfiguration
@IntegrationTest
class MyTestSpec extends Specification {
    …
}
  

Обновление: просто чтобы убедиться, что это понятно (и, судя по комментариям, это не так), чтобы это работало, вам нужно указать путь к org.spockframework:spock-spring классу.

Комментарии:

1. Интересно, я уверен, что я пробовал это раньше, и это не сработало … но в любом случае, спасибо!

2. Есть ли флаг для предоставления виртуальной машины при запуске интеграционных тестов? Я скопировал эту конфигурацию, и мое приложение не запускается. Правильно ли я делаю вывод, что MyServer — это класс, имеющий аннотации Configuration и EnableAutoConfiguration?

3. Никаких флагов не требуется, и ваш вывод о MyServer верен. Вы можете прочитать больше о @ContextConfiguration здесь: docs.spring.io/spring/docs/4.0.x/spring-framework-reference /. … Есть ли у вас spock-spring в пути к классу?

4. Привет @AndyWilkinson Я просто возвращался, чтобы сообщить людям, что spock-spring необходим в пути к классам, и я увидел ваш ответ. Спасибо, чувак!

5. Я обновил свой ответ, чтобы сделать его более понятным. Я также открыл проблему ( github.com/spring-projects/spring-boot/issues/1234 ) для обновления соответствующего раздела документации Spring Boot ( docs.spring.io/spring-boot/docs/1.1.4.RELEASE/reference /… )

Ответ №2:

В идеале вы должны использовать Spring Boot 1.4 и Spock 1.1 .

Spring Boot добавил много полезных аннотаций. В дополнение к тому @SpringBootTest , что упомянул @ignacio.suay, они также добавили @TestConfiguration , что полезно, если вы хотите использовать Spring mocks в своих интеграционных тестах вместо Mockito.

Если вы объединитесь @TestConfiguration с новым Spock DetachedMockFactory , то у вас будут все компоненты, необходимые для внедрения Spock Mocks в ваш Spring context.

У меня есть сообщение в блоге с примером кода здесь: 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 для внедрения Spock Mocks в контекст Spring для интеграционного тестирования. Новый @SpringBean и @SpringSpy будет похож на @MockBean @SpyBean аннотации and

ОБНОВЛЕНИЕ Spock 1.2 теперь должно включать эти изменения. Пока документация не будет обновлена, вот предварительный просмотр аннотаций Spock 1.2 для тестирования интеграции Spring .

Комментарии:

1. TestConfiguration можно использовать для определения дополнительных компонентов или настроек для теста, mocking — это только один из многих вариантов использования. Вы можете просто использовать spring @MockBean для моделирования использования Mockito….

2. @MariuszS Этот вопрос касается Спока. Использование Mockito в сочетании со Spock считалось бы довольно неприятным запахом кода. В Spock встроена насмешливая сборка. На момент этого ответа @MockBean не поддерживал Spock Mocks.

Ответ №3:

В новой версии 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 /

Ответ №4:

Вот настройка, которая запускает загрузочное приложение, а затем запускает тесты 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'
}
}
  

И не забудьте добавить зависимости для spock и groovy внутри build.gradle

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