java - Prueba de integración con Spring Boot y Spock
groovy gradle (4)
¿Cuál es la mejor manera de ejecutar una prueba de integración (por ejemplo, @IntegrationTest
) con Spock? Me gustaría iniciar la aplicación Spring Boot y ejecutar algunas llamadas HTTP para probar toda la funcionalidad.
Puedo hacerlo con JUnit (primero se ejecuta la aplicación y luego se ejecutan las pruebas):
@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
}
}
Pero con Spock, la aplicación no se inicia:
@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
}
}
Para Spock, por supuesto, he especificado las dependencias adecuadas en mi archivo de compilación Gradle:
...
dependencies {
testCompile ''org.spockframework:spock-core:0.7-groovy-2.0''
testCompile ''org.spockframework:spock-spring:0.7-groovy-2.0''
}
...
¿Me estoy perdiendo de algo?
Aquí hay una configuración que inicia la aplicación de arranque y luego ejecuta las pruebas de 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''
}
}
Y recuerda agregar dependencias a spock y groovy dentro de build.gradle
dependencies {
// other dependencies
testCompile "org.codehaus.groovy:groovy-all:2.2.0"
testCompile "org.spockframework:spock-core:0.7-groovy-2.0"
}
El problema es que Spock Spring está buscando la anotación @ContextConfiguration
de Spring y no logra encontrarla. Estrictamente hablando, MyTestSpec
está anotado con @ContextConfiguration
ya que es una meta-anotación en @SpringApplicationConfiguration
pero Spock Spring no considera las @SpringApplicationConfiguration
como parte de su búsqueda. Hay un issue para abordar esta limitación. Mientras tanto, puedes evitarlo.
Todo lo que @SpringApplicationConfiguration
está haciendo es personalizar @ContextConfiguration
con un cargador de contexto específico de Boot. Esto significa que puede lograr el mismo efecto utilizando en su lugar una anotación @ContextConfiguration
configurada adecuadamente:
@ContextConfiguration(loader = SpringApplicationContextLoader.class, classes = MyServer.class)
@WebAppConfiguration
@IntegrationTest
class MyTestSpec extends Specification {
…
}
Actualización: solo para asegurarse de que está claro (y basado en los comentarios, no fue así), para que esto funcione, debe tener org.spockframework:spock-spring
en classpath.
En la nueva versión de Spring Boot (1.4) en lugar de usar:
@SpringApplicationConfiguration(classes = MyServer.class)
@WebAppConfiguration
@IntegrationTest
Podrías usar
@SpringBootTest(classes = MyServer.class)
y podrá iniciar el contexto de la aplicación y establecer cualquier dependencia.
para obtener más información, consulte este ejemplo: http://ignaciosuay.com/how-to-do-integration-tests-with-spring-boot-and-spock/
Idealmente usará Spring Boot 1.4+ y Spock 1.1+.
Spring Boot agregó muchas anotaciones útiles. Además de ese @SpringBootTest
que mencionó @ ignacio.suay, también agregaron @TestConfiguration
que es útil si quiere usar los simulacros de Spring en sus pruebas de integración en lugar de Mockito.
Si combina @TestConfiguration
con el nuevo Spock DetachedMockFactory
, entonces tiene todos los componentes que necesitará para inyectar Spock Mocks en su contexto de Spring.
Tengo una publicación de blog con código de ejemplo aquí: Spring Integration Testing with Spock Mocks .
Lo rápido y sucio es esto
@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)
}
}
}
ACTUALIZACIÓN Existe un PR para obtener más soporte nativo en Spock para inyectar Spock Mocks en el contexto de Spring para pruebas de integración. El nuevo @SpringBean
y @SpringSpy
serían como las anotaciones @MockBean
y @SpyBean