spockframework / spock

The Enterprise-ready testing and specification framework.
https://spockframework.org
Apache License 2.0
3.55k stars 468 forks source link

Context not properly loaded with Spring Boot 3 for WebMvcTest with OpenFeign clients #1561

Open lmouline opened 1 year ago

lmouline commented 1 year ago

Describe the bug

When migration Spring Boot to version 3 and Spock framework to version 2.4-M1 with OpenFeign client(s), tests with the WebMvcTest annotation fail to properly load the context.

Please note that the tests with @SpringBootTest are executed successfully.

Seems that the issue also happens for tests with the @DataMongoTest annotation.

To Reproduce

testImplementation("org.springframework.boot:spring-boot-starter-test")

testImplementation("org.apache.groovy:groovy:4.0.7") testImplementation(platform("org.spockframework:spock-bom:2.4-M1-groovy-4.0")) testImplementation("org.spockframework:spock-core") testImplementation("org.spockframework:spock-spring")

- Create a dummy Rest controller that returns whatever you want
```kotlin
@RequestMapping("/dummy")
@RestController
class DummyController {

    @GetMapping
    fun getAllInformation() = listOf(
            Information("info1", "Great information!"),
            Information("info2", "Super important information!"),
            Information("info3", "Rather small information."),
    )

}

Minimal example with a failing test: demo.zip

Expected behavior

The test should pass

Actual behavior

The test fails with the following cause:

Caused by: org.springframework.beans.factory.NoSuchBeanDefinitionException: No qualifying bean of type 'org.springframework.cloud.openfeign.FeignClientFactory' available

(Note that the test with the @SpringBootTest annotation passes :))

Java version

17

Buildtool version

Gradle 7.6

What operating system are you using

Mac

Dependencies


Root project 'demo'

testRuntimeClasspath - Runtime classpath of compilation 'test' (target  (jvm)).
+--- org.springframework.boot:spring-boot-starter-web -> 3.0.1
|    +--- org.springframework.boot:spring-boot-starter:3.0.1
|    |    +--- org.springframework.boot:spring-boot:3.0.1
|    |    |    +--- org.springframework:spring-core:6.0.3
|    |    |    |    \--- org.springframework:spring-jcl:6.0.3
|    |    |    \--- org.springframework:spring-context:6.0.3
|    |    |         +--- org.springframework:spring-aop:6.0.3
|    |    |         |    +--- org.springframework:spring-beans:6.0.3
|    |    |         |    |    \--- org.springframework:spring-core:6.0.3 (*)
|    |    |         |    \--- org.springframework:spring-core:6.0.3 (*)
|    |    |         +--- org.springframework:spring-beans:6.0.3 (*)
|    |    |         +--- org.springframework:spring-core:6.0.3 (*)
|    |    |         \--- org.springframework:spring-expression:6.0.3
|    |    |              \--- org.springframework:spring-core:6.0.3 (*)
|    |    +--- org.springframework.boot:spring-boot-autoconfigure:3.0.1
|    |    |    \--- org.springframework.boot:spring-boot:3.0.1 (*)
|    |    +--- org.springframework.boot:spring-boot-starter-logging:3.0.1
|    |    |    +--- ch.qos.logback:logback-classic:1.4.5
|    |    |    |    +--- ch.qos.logback:logback-core:1.4.5
|    |    |    |    \--- org.slf4j:slf4j-api:2.0.4 -> 2.0.6
|    |    |    +--- org.apache.logging.log4j:log4j-to-slf4j:2.19.0
|    |    |    |    +--- org.slf4j:slf4j-api:1.7.36 -> 2.0.6
|    |    |    |    \--- org.apache.logging.log4j:log4j-api:2.19.0
|    |    |    \--- org.slf4j:jul-to-slf4j:2.0.6
|    |    |         \--- org.slf4j:slf4j-api:2.0.6
|    |    +--- jakarta.annotation:jakarta.annotation-api:2.1.1
|    |    +--- org.springframework:spring-core:6.0.3 (*)
|    |    \--- org.yaml:snakeyaml:1.33
|    +--- org.springframework.boot:spring-boot-starter-json:3.0.1
|    |    +--- org.springframework.boot:spring-boot-starter:3.0.1 (*)
|    |    +--- org.springframework:spring-web:6.0.3
|    |    |    +--- org.springframework:spring-beans:6.0.3 (*)
|    |    |    +--- org.springframework:spring-core:6.0.3 (*)
|    |    |    \--- io.micrometer:micrometer-observation:1.10.2
|    |    |         \--- io.micrometer:micrometer-commons:1.10.2
|    |    +--- com.fasterxml.jackson.core:jackson-databind:2.14.1
|    |    |    +--- com.fasterxml.jackson.core:jackson-annotations:2.14.1
|    |    |    |    \--- com.fasterxml.jackson:jackson-bom:2.14.1
|    |    |    |         +--- com.fasterxml.jackson.core:jackson-annotations:2.14.1 (c)
|    |    |    |         +--- com.fasterxml.jackson.core:jackson-databind:2.14.1 (c)
|    |    |    |         +--- com.fasterxml.jackson.datatype:jackson-datatype-jdk8:2.14.1 (c)
|    |    |    |         +--- com.fasterxml.jackson.datatype:jackson-datatype-jsr310:2.14.1 (c)
|    |    |    |         +--- com.fasterxml.jackson.module:jackson-module-kotlin:2.14.1 (c)
|    |    |    |         +--- com.fasterxml.jackson.module:jackson-module-parameter-names:2.14.1 (c)
|    |    |    |         \--- com.fasterxml.jackson.core:jackson-core:2.14.1 (c)
|    |    |    +--- com.fasterxml.jackson.core:jackson-core:2.14.1
|    |    |    |    \--- com.fasterxml.jackson:jackson-bom:2.14.1 (*)
|    |    |    \--- com.fasterxml.jackson:jackson-bom:2.14.1 (*)
|    |    +--- com.fasterxml.jackson.datatype:jackson-datatype-jdk8:2.14.1
|    |    |    +--- com.fasterxml.jackson.core:jackson-core:2.14.1 (*)
|    |    |    +--- com.fasterxml.jackson.core:jackson-databind:2.14.1 (*)
|    |    |    \--- com.fasterxml.jackson:jackson-bom:2.14.1 (*)
|    |    +--- com.fasterxml.jackson.datatype:jackson-datatype-jsr310:2.14.1
|    |    |    +--- com.fasterxml.jackson.core:jackson-annotations:2.14.1 (*)
|    |    |    +--- com.fasterxml.jackson.core:jackson-core:2.14.1 (*)
|    |    |    +--- com.fasterxml.jackson.core:jackson-databind:2.14.1 (*)
|    |    |    \--- com.fasterxml.jackson:jackson-bom:2.14.1 (*)
|    |    \--- com.fasterxml.jackson.module:jackson-module-parameter-names:2.14.1
|    |         +--- com.fasterxml.jackson.core:jackson-core:2.14.1 (*)
|    |         +--- com.fasterxml.jackson.core:jackson-databind:2.14.1 (*)
|    |         \--- com.fasterxml.jackson:jackson-bom:2.14.1 (*)
|    +--- org.springframework.boot:spring-boot-starter-tomcat:3.0.1
|    |    +--- jakarta.annotation:jakarta.annotation-api:2.1.1
|    |    +--- org.apache.tomcat.embed:tomcat-embed-core:10.1.4
|    |    +--- org.apache.tomcat.embed:tomcat-embed-el:10.1.4
|    |    \--- org.apache.tomcat.embed:tomcat-embed-websocket:10.1.4
|    |         \--- org.apache.tomcat.embed:tomcat-embed-core:10.1.4
|    +--- org.springframework:spring-web:6.0.3 (*)
|    \--- org.springframework:spring-webmvc:6.0.3
|         +--- org.springframework:spring-aop:6.0.3 (*)
|         +--- org.springframework:spring-beans:6.0.3 (*)
|         +--- org.springframework:spring-context:6.0.3 (*)
|         +--- org.springframework:spring-core:6.0.3 (*)
|         +--- org.springframework:spring-expression:6.0.3 (*)
|         \--- org.springframework:spring-web:6.0.3 (*)
+--- com.fasterxml.jackson.module:jackson-module-kotlin -> 2.14.1
|    +--- com.fasterxml.jackson.core:jackson-databind:2.14.1 (*)
|    +--- com.fasterxml.jackson.core:jackson-annotations:2.14.1 (*)
|    +--- org.jetbrains.kotlin:kotlin-reflect:1.5.32 -> 1.7.22
|    |    \--- org.jetbrains.kotlin:kotlin-stdlib:1.7.22
|    |         +--- org.jetbrains.kotlin:kotlin-stdlib-common:1.7.22
|    |         \--- org.jetbrains:annotations:13.0
|    \--- com.fasterxml.jackson:jackson-bom:2.14.1 (*)
+--- org.jetbrains.kotlin:kotlin-reflect:1.7.22 (*)
+--- org.jetbrains.kotlin:kotlin-stdlib-jdk8:1.7.22
|    +--- org.jetbrains.kotlin:kotlin-stdlib:1.7.22 (*)
|    \--- org.jetbrains.kotlin:kotlin-stdlib-jdk7:1.7.22
|         \--- org.jetbrains.kotlin:kotlin-stdlib:1.7.22 (*)
+--- org.springframework.cloud:spring-cloud-starter-openfeign -> 4.0.0
|    +--- org.springframework.cloud:spring-cloud-starter:4.0.0
|    |    +--- org.springframework.boot:spring-boot-starter:3.0.0 -> 3.0.1 (*)
|    |    +--- org.springframework.cloud:spring-cloud-context:4.0.0
|    |    |    \--- org.springframework.security:spring-security-crypto:6.0.0 -> 6.0.1
|    |    +--- org.springframework.cloud:spring-cloud-commons:4.0.0
|    |    |    \--- org.springframework.security:spring-security-crypto:6.0.0 -> 6.0.1
|    |    \--- org.springframework.security:spring-security-rsa:1.0.11.RELEASE
|    |         \--- org.bouncycastle:bcpkix-jdk15on:1.69
|    |              +--- org.bouncycastle:bcprov-jdk15on:1.69
|    |              \--- org.bouncycastle:bcutil-jdk15on:1.69
|    |                   \--- org.bouncycastle:bcprov-jdk15on:1.69
|    +--- org.springframework.cloud:spring-cloud-openfeign-core:4.0.0
|    |    +--- org.springframework.boot:spring-boot-autoconfigure:3.0.0 -> 3.0.1 (*)
|    |    +--- org.springframework.boot:spring-boot-starter-aop:3.0.0 -> 3.0.1
|    |    |    +--- org.springframework.boot:spring-boot-starter:3.0.1 (*)
|    |    |    +--- org.springframework:spring-aop:6.0.3 (*)
|    |    |    \--- org.aspectj:aspectjweaver:1.9.19
|    |    \--- io.github.openfeign.form:feign-form-spring:3.8.0
|    |         +--- io.github.openfeign.form:feign-form:3.8.0
|    |         |    \--- org.slf4j:slf4j-api:1.7.26 -> 2.0.6
|    |         +--- org.springframework:spring-web:5.1.5.RELEASE -> 6.0.3 (*)
|    |         +--- commons-fileupload:commons-fileupload:1.4
|    |         \--- org.slf4j:slf4j-api:1.7.26 -> 2.0.6
|    +--- org.springframework:spring-web:6.0.2 -> 6.0.3 (*)
|    +--- org.springframework.cloud:spring-cloud-commons:4.0.0 (*)
|    +--- io.github.openfeign:feign-core:12.1
|    \--- io.github.openfeign:feign-slf4j:12.1
|         +--- io.github.openfeign:feign-core:12.1
|         \--- org.slf4j:slf4j-api:2.0.4 -> 2.0.6
+--- org.springframework.boot:spring-boot-starter-test -> 3.0.1
|    +--- org.springframework.boot:spring-boot-starter:3.0.1 (*)
|    +--- org.springframework.boot:spring-boot-test:3.0.1
|    |    \--- org.springframework.boot:spring-boot:3.0.1 (*)
|    +--- org.springframework.boot:spring-boot-test-autoconfigure:3.0.1
|    |    +--- org.springframework.boot:spring-boot:3.0.1 (*)
|    |    +--- org.springframework.boot:spring-boot-test:3.0.1 (*)
|    |    \--- org.springframework.boot:spring-boot-autoconfigure:3.0.1 (*)
|    +--- com.jayway.jsonpath:json-path:2.7.0
|    |    +--- net.minidev:json-smart:2.4.7 -> 2.4.8
|    |    |    \--- net.minidev:accessors-smart:2.4.8
|    |    |         \--- org.ow2.asm:asm:9.1
|    |    \--- org.slf4j:slf4j-api:1.7.33 -> 2.0.6
|    +--- jakarta.xml.bind:jakarta.xml.bind-api:4.0.0
|    |    \--- jakarta.activation:jakarta.activation-api:2.1.0
|    +--- org.assertj:assertj-core:3.23.1
|    |    \--- net.bytebuddy:byte-buddy:1.12.10 -> 1.12.20
|    +--- org.hamcrest:hamcrest:2.2
|    +--- org.junit.jupiter:junit-jupiter:5.9.1
|    |    +--- org.junit:junit-bom:5.9.1
|    |    |    +--- org.junit.jupiter:junit-jupiter:5.9.1 (c)
|    |    |    +--- org.junit.jupiter:junit-jupiter-api:5.9.1 (c)
|    |    |    +--- org.junit.jupiter:junit-jupiter-engine:5.9.1 (c)
|    |    |    +--- org.junit.jupiter:junit-jupiter-params:5.9.1 (c)
|    |    |    +--- org.junit.platform:junit-platform-engine:1.9.1 (c)
|    |    |    \--- org.junit.platform:junit-platform-commons:1.9.1 (c)
|    |    +--- org.junit.jupiter:junit-jupiter-api:5.9.1
|    |    |    +--- org.junit:junit-bom:5.9.1 (*)
|    |    |    +--- org.opentest4j:opentest4j:1.2.0
|    |    |    \--- org.junit.platform:junit-platform-commons:1.9.1
|    |    |         \--- org.junit:junit-bom:5.9.1 (*)
|    |    +--- org.junit.jupiter:junit-jupiter-params:5.9.1
|    |    |    +--- org.junit:junit-bom:5.9.1 (*)
|    |    |    \--- org.junit.jupiter:junit-jupiter-api:5.9.1 (*)
|    |    \--- org.junit.jupiter:junit-jupiter-engine:5.9.1
|    |         +--- org.junit:junit-bom:5.9.1 (*)
|    |         +--- org.junit.platform:junit-platform-engine:1.9.1
|    |         |    +--- org.junit:junit-bom:5.9.1 (*)
|    |         |    +--- org.opentest4j:opentest4j:1.2.0
|    |         |    \--- org.junit.platform:junit-platform-commons:1.9.1 (*)
|    |         \--- org.junit.jupiter:junit-jupiter-api:5.9.1 (*)
|    +--- org.mockito:mockito-core:4.8.1
|    |    +--- net.bytebuddy:byte-buddy:1.12.16 -> 1.12.20
|    |    +--- net.bytebuddy:byte-buddy-agent:1.12.16 -> 1.12.20
|    |    \--- org.objenesis:objenesis:3.2
|    +--- org.mockito:mockito-junit-jupiter:4.8.1
|    |    +--- org.mockito:mockito-core:4.8.1 (*)
|    |    \--- org.junit.jupiter:junit-jupiter-api:5.9.1 (*)
|    +--- org.skyscreamer:jsonassert:1.5.1
|    |    \--- com.vaadin.external.google:android-json:0.0.20131108.vaadin1
|    +--- org.springframework:spring-core:6.0.3 (*)
|    +--- org.springframework:spring-test:6.0.3
|    |    \--- org.springframework:spring-core:6.0.3 (*)
|    \--- org.xmlunit:xmlunit-core:2.9.0
+--- org.apache.groovy:groovy:4.0.7
|    \--- org.apache.groovy:groovy-bom:4.0.7
|         \--- org.apache.groovy:groovy:4.0.7 (c)
+--- org.spockframework:spock-bom:2.4-M1-groovy-4.0
|    +--- org.spockframework:spock-core:2.4-M1-groovy-4.0 (c)
|    \--- org.spockframework:spock-spring:2.4-M1-groovy-4.0 (c)
+--- org.spockframework:spock-core -> 2.4-M1-groovy-4.0
|    +--- org.apache.groovy:groovy:4.0.6 -> 4.0.7 (*)
|    +--- org.junit:junit-bom:5.9.0 -> 5.9.1 (*)
|    +--- org.junit.platform:junit-platform-engine -> 1.9.1 (*)
|    \--- org.hamcrest:hamcrest:2.2
\--- org.spockframework:spock-spring -> 2.4-M1-groovy-4.0
     +--- org.apache.groovy:groovy:4.0.6 -> 4.0.7 (*)
     \--- org.spockframework:spock-core:2.4-M1-groovy-4.0 (*)

(c) - dependency constraint
(*) - dependencies omitted (listed previously)

A web-based, searchable dependency report is available by adding the --scan option.

Additional context

No response

leonard84 commented 1 year ago

And this works with prior Spring Boot versions? IIRC @WebMvcTest([DummyController.class]) restricts loading of beans, so I'd assume that the @EnableFeignClients is not picked up and you need to explicitly add it to the context.

As you can see by the error message:

Caused by: org.springframework.beans.factory.NoSuchBeanDefinitionException: No qualifying bean of type 'org.springframework.cloud.openfeign.FeignClientFactory' available

This means that Spock did correctly invoke the Spring testing infrastructure.

szpak commented 1 year ago

It might be related to the old thread: https://github.com/spring-projects/spring-boot/issues/7270#issuecomment-332525848.

szpak commented 1 year ago

I've bumped into that case recently. The weird thing is, it worked fine with Spring Boot 2 and Spock 2.3. However, I clearly see that in SB2 the Feign client (GoogleClient in your case) is not instantiated by default (when I try to inject it explicitly into my test, I have No qualifying bean of type 'org.springframework.cloud.openfeign.FeignContext' available). After the migration to SB3 - it is instantiated automatically with the same @WebMvcTest configuration which generates the problem.

@lmouline Could you write one test with JUnit Jupiter and the aforementioned @WebMvcTest configuration to check if it still fails? If yes, I would be looking for the changes in Spring Boot 3 (or some underlying Spring components) as a reason.

lmouline commented 1 year ago

@leonard84

And this works with prior Spring Boot versions?

Yep it does :)
From the demo project, using the following dependencies (without changing the code) makes it pass:

plugins {
    id("org.springframework.boot") version "2.7.8"
} 
extra["springCloudVersion"] = "2021.0.5"

dependencies {
   testImplementation(platform("org.spockframework:spock-bom:2.3-groovy-4.0"))
}

@szpak

Could you write one test with JUnit Jupiter and the aforementioned @WebMvcTest configuration to check if it still fails?

The test with Junit works :( Here a new version of a demo with both the Spock test and the JUnit one. They are strictly identical. But you can see that only the JUnit one works :/ demo.zip

leonard84 commented 1 year ago

Honestly, not sure what the cause is, as Spock is not really involved in that part. It just instantiates Spring's TestContextManager with the Specification and calls it's lifecycle methods at the correct time. Everything else should be handled by Spring (Boot) internals.