spring-projects / spring-framework

Spring Framework
https://spring.io/projects/spring-framework
Apache License 2.0
56.69k stars 38.15k forks source link

ClassNotFoundException: ExchangeFunction when using WebTestClient with Spring MVC #26308

Closed pkubowicz closed 3 years ago

pkubowicz commented 3 years ago

After reading release notes for Spring Framework 5.3 and the Reference Documentation I wanted to use WebTestClient in a Spring MVC application, but it fails with:

Injection of autowired dependencies failed; nested exception is java.lang.NoClassDefFoundError: org/springframework/web/reactive/function/client/ExchangeFunction

Caused by: java.lang.NoClassDefFoundError: org/springframework/web/reactive/function/client/ExchangeFunction
    at org.springframework.test.web.reactive.server.DefaultWebTestClientBuilder.build(DefaultWebTestClientBuilder.java:268)
    at com.example.mvcwebtestclient.GreetingControllerTest.create(GreetingControllerTest.java:23)
    at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke0(Native Method)

Reproducing

build.gradle

dependencies {
    implementation(platform("org.springframework.boot:spring-boot-dependencies:2.4.1"))
    implementation 'org.springframework.boot:spring-boot-starter-data-mongodb-reactive'
    implementation 'org.springframework.boot:spring-boot-starter-web'
    testImplementation 'org.springframework.boot:spring-boot-starter-test'
    testImplementation 'io.projectreactor:reactor-test'
}

GreetingController.java

@RestController
@RequestMapping("/greetings")
public class GreetingController {

    @GetMapping
    public Flux<String> findAll() {
        return Flux.just("Hello", "world!");
    }
}

GreetingControllerTest.java

@SpringBootTest
class GreetingControllerTest {

    private WebTestClient client;

    @Autowired
    public void create(WebApplicationContext context) {
        client = MockMvcWebTestClient.bindToApplicationContext(context)
                .configureClient()
                .build();
    }

    @Test
    public void findsAll() {
        client.get().uri("/greetings")
                .exchange()
                .expectStatus().isOk();
    }
}

Workaround

Add webflux dependency:

implementation 'org.springframework.boot:spring-boot-starter-webflux'

I think it's a bug in code or at least a bug in documentation. The documentation does not mention in any place that you need WebFlux to test your MVC applications. This is an idea that looks absurd at first sight, so if it is really the case, it should be explicitly explained that you really need to do it.

snicoll commented 3 years ago

WebTestClient is a test wrapper around WebClient. The very first link of the section you've referenced points to the WebClientsection that's part of Spring Webflux. I've transferred your issue to Spring Framework to determine if the doc could be more explicit about this.

wagnerluis1982 commented 3 years ago

Rather than only improving documentation, it would be much better if Spring could show a better message to the user.

Something in the lines of

Injection ... failed; you probably wanted to declare webflux dependencies in your pom.xml or build.gradle

Caused by: java.lang.NoClassDefFoundError: org/springframework/web/reactive/function/client/ExchangeFunction
    at ...