#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"
}