2

当应用程序启动并从测试中访问时,Spring Boot / Jersey 找不到处理程序方法。如果我单独启动应用程序并使用http://localhost:8080/demo浏览器访问一切都很好。

日志说:“没有找到 [/demo] 的处理程序方法”。相关的日志输出:

2018-06-18 17:04:31.071 DEBUG 7628 --- [nio-8080-exec-1] o.s.web.reactive.DispatcherHandler       : Processing GET request for [http://localhost:8080/demo]
2018-06-18 17:04:31.083 DEBUG 7628 --- [nio-8080-exec-1] s.w.r.r.m.a.RequestMappingHandlerMapping : Looking up handler method for path /demo
2018-06-18 17:04:31.085 DEBUG 7628 --- [nio-8080-exec-1] s.w.r.r.m.a.RequestMappingHandlerMapping : Did not find handler method for [/demo]
2018-06-18 17:04:31.087 DEBUG 7628 --- [nio-8080-exec-1] o.s.w.r.handler.SimpleUrlHandlerMapping  : Matching pattern for request [[path='/demo']] is /**

该应用程序包含以下类(用 Kotlin 编写):

资源

import org.springframework.stereotype.Component
import javax.ws.rs.GET
import javax.ws.rs.Path
import javax.ws.rs.core.Response

@Component
@Path("/")
class Resource {

    @GET
    @Path("demo")
    fun test() = Response.ok("Hi!").encoding("UTF-8").build()
}

JerseyConfig

import org.glassfish.jersey.server.ResourceConfig
import org.springframework.stereotype.Component

@Component
class JerseyConfig : ResourceConfig() {

    init {
        register(Resource::class.java)
    }
}

应用程序:

import org.springframework.boot.autoconfigure.SpringBootApplication
import org.springframework.boot.runApplication

@SpringBootApplication
class App

fun main(args: Array<String>) {
    runApplication<App>(*args)
}

失败的测试:

import org.junit.jupiter.api.Test
import org.junit.jupiter.api.TestInstance
import org.junit.jupiter.api.extension.ExtendWith
import org.springframework.beans.factory.annotation.Autowired
import org.springframework.boot.test.context.SpringBootTest
import org.springframework.test.context.junit.jupiter.SpringExtension
import org.springframework.test.web.reactive.server.WebTestClient

@ExtendWith(SpringExtension::class)
@TestInstance(TestInstance.Lifecycle.PER_CLASS)
@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.DEFINED_PORT)
class ResourceTest {

    @Autowired
    lateinit var client: WebTestClient

    @Test
    fun getTest() {
        client.get().uri("demo").exchange().expectStatus().isOk
    }
}

如果我使用 Jersey 客户端进行测试,我会得到同样的错误:

@Test
fun testWithJersey() {
    val client = ClientBuilder.newClient()
    val response = client.target("http://localhost:8080/demo").request().get()
    assertThat(response.status).isEqualTo(200)
}

构建.gradle:

buildscript {
    ext {
        kotlinVersion = '1.2.50'
        springBootVersion = '2.0.3.RELEASE'
    }
    repositories {
        mavenCentral()
    }
    dependencies {
        classpath("org.springframework.boot:spring-boot-gradle-plugin:${springBootVersion}")
        classpath("org.jetbrains.kotlin:kotlin-gradle-plugin:${kotlinVersion}")
        classpath("org.jetbrains.kotlin:kotlin-allopen:${kotlinVersion}")
    }
}

apply plugin: 'kotlin'
apply plugin: 'kotlin-spring'
apply plugin: 'org.springframework.boot'
apply plugin: 'io.spring.dependency-management'

compileKotlin {
    kotlinOptions {
        freeCompilerArgs = ["-Xjsr305=strict"]
        jvmTarget = "1.8"
    }
}
compileTestKotlin {
    kotlinOptions {
        freeCompilerArgs = ["-Xjsr305=strict"]
        jvmTarget = "1.8"
    }
}

repositories {
    mavenCentral()
}

dependencies {
    compile("org.jetbrains.kotlin:kotlin-stdlib-jdk8")
    compile("org.jetbrains.kotlin:kotlin-reflect")
    compile('org.springframework.boot:spring-boot-starter-jersey')
    testCompile("org.springframework.boot:spring-boot-starter-test") {
        exclude group: "junit", module: "junit"
    }
    testCompile('org.springframework.boot:spring-boot-starter-webflux')
    testCompile("org.junit.jupiter:junit-jupiter-api")
    testRuntime("org.junit.jupiter:junit-jupiter-engine")
}

测试代码本身似乎没问题,因为当我将测试方法的主体替换为Thread.sleep(...)然后从浏览器访问服务器时,我得到了同样的错误(404,由于“没有找到 [/demo] 的处理程序方法” )。

为什么在测试中找不到处理程序方法?我必须改变什么?

4

2 回答 2

1

在来自 Pivotal 的了不起的家伙的帮助下,我解决了这个问题:WebTestClient需要WebFlux作为依赖。但是 WebFlux 不仅带来了一个小测试助手,而且带来了一个完整的 Web 框架,它由 Spring Boot 自动配置,并且与 Jersey 冲突。

解决方案是从依赖项中删除 WebFlux

dependencies {
    compile("org.jetbrains.kotlin:kotlin-stdlib-jdk8")
    compile("org.jetbrains.kotlin:kotlin-reflect")
    compile('org.springframework.boot:spring-boot-starter-jersey')
    testCompile("org.springframework.boot:spring-boot-starter-test") {
        exclude group: "junit", module: "junit"
    }
    testCompile("org.junit.jupiter:junit-jupiter-api")
    testRuntime("org.junit.jupiter:junit-jupiter-engine")
}

并使用例如 Jersey http 客户端:

@ExtendWith(SpringExtension::class)
@TestInstance(TestInstance.Lifecycle.PER_CLASS)
@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.DEFINED_PORT)
class ResourceTest {

    @Test
    fun testWithJerseyClient() {
        val client = ClientBuilder.newClient()
        val response = client.target("http://localhost:8080/demo").request().get()
        assertThat(response.status).isEqualTo(200)
    }
}

但只要 WebFlux 不是依赖项,任何其他 http 客户端都可以。

更新:这是 Spring Boot 中的一个错误,将在 Spring Boot 2.0.4 中修复!然后,您可以在服务器端使用 WebFlux 和 Jersey 进行测试。

于 2018-06-19T09:17:47.323 回答
1

您确定WebTestClient是一个通用客户端(发出实际的网络请求),并且不仅像 Spring MVC 那样与 Spring MVC 一起使用MockMvc吗?该错误听起来像是在寻找 Spring MVC 处理程序方法。如果它是一个通用客户端,则错误消息不会说明处理程序方法,而是可能会说明 URL。

我想你需要用一个真实的客户端发出一个实际的网络请求。例如,如果您使用 Jersey 客户端,您可以执行类似的操作

@LocalServerPort
private int port;

private Client client = ClientBuilder.newClient();

@Test
public void testCustomerLocationOnPost() {
    URI resourceUri = UriBuilder.fromUri("http://localhost")
            .port(port).path("demo").build();

    Respons response = client.target(resourcrUri).request().get();

    assertThat(response.getStatus()).isEqualTo(200);

}
于 2018-06-18T19:16:44.930 回答