Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

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

Open
lmouline opened this issue Jan 19, 2023 · 5 comments

Comments

@lmouline
Copy link

lmouline commented Jan 19, 2023

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

  • Create a Spring Boot 3 project with OpenFeign
implementation("org.springframework.boot:spring-boot-starter-web")
implementation("com.fasterxml.jackson.module:jackson-module-kotlin")
implementation("org.jetbrains.kotlin:kotlin-reflect")
implementation("org.jetbrains.kotlin:kotlin-stdlib-jdk8")
implementation("org.springframework.cloud:spring-cloud-starter-openfeign")


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
@RequestMapping("/dummy")
@RestController
class DummyController {

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

}
  • Create a dummy Feign client
@FeignClient(
   name = "google",
   url = "https://www.google.com/"
)
interface GoogleClient {
    @GetMapping("/")
    fun getHomePage(): String
}
  • Create a WebMvcTest that test the controller
@WebMvcTest([DummyController.class])
class DummyControllerSpec extends Specification {

    @Autowired
    MockMvc mockMvc


    def "get information"() {
        expect:
        mockMvc.perform(get("/dummy"))
            .andExpect(status().is2xxSuccessful())

    }
}

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

@lmouline lmouline added the bug label Jan 19, 2023
@leonard84
Copy link
Member

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
Copy link
Member

szpak commented Feb 16, 2023

It might be related to the old thread: spring-projects/spring-boot#7270 (comment).

@szpak
Copy link
Member

szpak commented Feb 16, 2023

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
Copy link
Author

@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
Copy link
Member

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.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests

3 participants