Spring Boot + Spock + Rest Assured 的问题

Problems with Spring Boot + Spock + Rest Assured

我使用 Spring Boot、Spock 和 Rest Assured 进行了简单测试。

package com.xxx.mako.rrhh.persona.infrastructure.controller

import com.xxx.mako.IntegrationTest
import com.xxx.mako.MakoApplication
import io.restassured.http.ContentType
import org.junit.experimental.categories.Category
import org.springframework.beans.factory.annotation.Value
import org.springframework.boot.test.context.SpringBootTest
import org.springframework.test.context.ActiveProfiles
import org.springframework.test.context.TestPropertySource
import org.springframework.test.context.jdbc.Sql
import spock.lang.Specification

import io.restassured.RestAssured
import io.restassured.builder.ResponseSpecBuilder
import static io.restassured.RestAssured.*
import static org.hamcrest.Matchers.*

@ActiveProfiles("test")
@SpringBootTest(
    webEnvironment = SpringBootTest.WebEnvironment.DEFINED_PORT,
    classes = MakoApplication.class
)
@TestPropertySource(locations = "classpath:application-test.yml")
@Category(IntegrationTest.class)
@Sql(scripts = ["classpath:/createTablePersona.sql", "classpath:/insertTablePersona.sql"])
class PersonaRestAssuredSpec extends Specification {
    final static URL_REST_SERVICE = "/mako/api/rest/personas/v1"
    final static validToken = createHeaders("admin", "admin00")
    final static invalidToken = createHeaders("dummy", "dummy")
    final static SC_OK = 200
    final static SC_UNAUTHORIZED = 401

    @Value('${server.http.port}')
    private int port

    def setup() throws Exception {
        RestAssured.port = port
    }

    def "findById"() {
        given:
            def request = given().accept(ContentType.JSON).header("Authorization", token)
        when: "A rest for obtain the Persona object with idPersona, token"
            def response = request.with().get(URL_REST_SERVICE + "/" + idPersona)
        then: "We expected status and specification like in the parameterized list in where clause"
            response.then().statusCode(status).spec(specification)
        where:
            idPersona   || token        || status           || specification
            1000        || invalidToken || SC_UNAUTHORIZED  || expect().body("error", equalTo("Unauthorized"))
            2           || validToken   || SC_OK            || expect().body("id", equalTo("2"))
    }

    private static String createHeaders(String username, String password) {
        String auth = username + ":" + password
        String encodedAuth = Base64.getEncoder().encodeToString(auth.getBytes("utf-8"))
        String authHeader = "Basic " + encodedAuth
        return authHeader
    }
}

当我执行测试时出现此错误:

Condition failed with Exception:

response.then().statusCode(status).spec(specification)
|        |      |          |       |    |
|        |      |          401     |    <io.restassured.internal.ResponseSpecificationImpl@6f6a7463 expectedStatusCode=null expectedStatusLine=null bodyMatchers=io.restassured.assertion.BodyMatcherGroup@1e53135d assertionClosure=io.restassured.internal.ResponseSpecificationImpl$HamcrestAssertionClosure@7674a051 headerAssertions=[] cookieAssertions=[] requestSpecification=io.restassured.internal.RequestSpecificationImpl@3a7704c contentType=null restAssuredResponse=null bodyRootPath= rpr=io.restassured.internal.ResponseParserRegistrar@6754ef00 config=io.restassured.config.RestAssuredConfig@619bd14c response=null expectedResponseTime=null responseLogDetail=null forceDisableEagerAssert=false contentParser=null logRepository=io.restassured.internal.log.LogRepository@323e8306>
|        |      |                  groovy.lang.MissingMethodException: No signature of method: io.restassured.internal.ContentParser.parse() is applicable for argument types: (io.restassured.internal.RestAssuredResponseImpl, io.restassured.internal.ResponseParserRegistrar...) values: [io.restassured.internal.RestAssuredResponseImpl@51efdb72, io.restassured.internal.ResponseParserRegistrar@6754ef00, ...]
|        |      |                  Possible solutions: wait(), grep(), any()
|        |      |                   at io.restassured.internal.ResponseSpecificationImpl$HamcrestAssertionClosure.validate(ResponseSpecificationImpl.groovy:478)
|        |      |                   at io.restassured.internal.ResponseSpecificationImpl.validate(ResponseSpecificationImpl.groovy:92)
|        |      |                   at io.restassured.internal.ValidatableResponseOptionsImpl.spec(ValidatableResponseOptionsImpl.java:288)
|        |      |                   at io.restassured.internal.ValidatableResponseImpl.spec(ValidatableResponseImpl.groovy:76)
|        |      |                   at com.xxx.mako.rrhh.persona.infrastructure.controller.PersonaRestAssuredSpec.findById(PersonaRestAssuredSpec.groovy:47)
|        |      <io.restassured.internal.ValidatableResponseImpl@76cbee13 responseSpec=io.restassured.internal.ResponseSpecificationImpl@59d09ff3 extractableResponse=io.restassured.internal.RestAssuredResponseImpl@51efdb72 response=io.restassured.internal.RestAssuredResponseImpl@51efdb72 config=io.restassured.config.RestAssuredConfig@619bd14c>
|        <io.restassured.internal.ValidatableResponseImpl@76cbee13 responseSpec=io.restassured.internal.ResponseSpecificationImpl@59d09ff3 extractableResponse=io.restassured.internal.RestAssuredResponseImpl@51efdb72 response=io.restassured.internal.RestAssuredResponseImpl@51efdb72 config=io.restassured.config.RestAssuredConfig@619bd14c>
<io.restassured.internal.RestAssuredResponseImpl@51efdb72 logRepository=io.restassured.internal.log.LogRepository@7bc6b117 groovyResponse=io.restassured.internal.RestAssuredResponseOptionsGroovyImpl@2de07c57>


    at com.xxx.mako.rrhh.persona.infrastructure.controller.PersonaRestAssuredSpec.findById(PersonaRestAssuredSpec.groovy:47)
Caused by: groovy.lang.MissingMethodException: No signature of method: io.restassured.internal.ContentParser.parse() is applicable for argument types: (io.restassured.internal.RestAssuredResponseImpl, io.restassured.internal.ResponseParserRegistrar...) values: [io.restassured.internal.RestAssuredResponseImpl@51efdb72, io.restassured.internal.ResponseParserRegistrar@6754ef00, ...]
Possible solutions: wait(), grep(), any()
    at io.restassured.internal.ResponseSpecificationImpl$HamcrestAssertionClosure.validate(ResponseSpecificationImpl.groovy:478)
    at io.restassured.internal.ResponseSpecificationImpl.validate(ResponseSpecificationImpl.groovy:92)
    at io.restassured.internal.ValidatableResponseOptionsImpl.spec(ValidatableResponseOptionsImpl.java:288)
    at io.restassured.internal.ValidatableResponseImpl.spec(ValidatableResponseImpl.groovy:76)
    ... 1 more


Condition failed with Exception:

response.then().statusCode(status).spec(specification)
|        |      |          |       |    |
|        |      |          200     |    <io.restassured.internal.ResponseSpecificationImpl@7de43652 expectedStatusCode=null expectedStatusLine=null bodyMatchers=io.restassured.assertion.BodyMatcherGroup@7fe0ca60 assertionClosure=io.restassured.internal.ResponseSpecificationImpl$HamcrestAssertionClosure@fdf029a headerAssertions=[] cookieAssertions=[] requestSpecification=io.restassured.internal.RequestSpecificationImpl@63896cf7 contentType=null restAssuredResponse=null bodyRootPath= rpr=io.restassured.internal.ResponseParserRegistrar@3f838072 config=io.restassured.config.RestAssuredConfig@619bd14c response=null expectedResponseTime=null responseLogDetail=null forceDisableEagerAssert=false contentParser=null logRepository=io.restassured.internal.log.LogRepository@46c9ee28>
|        |      |                  groovy.lang.MissingMethodException: No signature of method: io.restassured.internal.ContentParser.parse() is applicable for argument types: (io.restassured.internal.RestAssuredResponseImpl, io.restassured.internal.ResponseParserRegistrar...) values: [io.restassured.internal.RestAssuredResponseImpl@263e512e, io.restassured.internal.ResponseParserRegistrar@3f838072, ...]
|        |      |                  Possible solutions: wait(), grep(), any()
|        |      |                   at io.restassured.internal.ResponseSpecificationImpl$HamcrestAssertionClosure.validate(ResponseSpecificationImpl.groovy:478)
|        |      |                   at io.restassured.internal.ResponseSpecificationImpl.validate(ResponseSpecificationImpl.groovy:92)
|        |      |                   at io.restassured.internal.ValidatableResponseOptionsImpl.spec(ValidatableResponseOptionsImpl.java:288)
|        |      |                   at io.restassured.internal.ValidatableResponseImpl.spec(ValidatableResponseImpl.groovy:76)
|        |      |                   at com.xxx.mako.rrhh.persona.infrastructure.controller.PersonaRestAssuredSpec.findById(PersonaRestAssuredSpec.groovy:47)
|        |      <io.restassured.internal.ValidatableResponseImpl@7c680fe1 responseSpec=io.restassured.internal.ResponseSpecificationImpl@288b8663 extractableResponse=io.restassured.internal.RestAssuredResponseImpl@263e512e response=io.restassured.internal.RestAssuredResponseImpl@263e512e config=io.restassured.config.RestAssuredConfig@619bd14c>
|        <io.restassured.internal.ValidatableResponseImpl@7c680fe1 responseSpec=io.restassured.internal.ResponseSpecificationImpl@288b8663 extractableResponse=io.restassured.internal.RestAssuredResponseImpl@263e512e response=io.restassured.internal.RestAssuredResponseImpl@263e512e config=io.restassured.config.RestAssuredConfig@619bd14c>
<io.restassured.internal.RestAssuredResponseImpl@263e512e logRepository=io.restassured.internal.log.LogRepository@62c42a3 groovyResponse=io.restassured.internal.RestAssuredResponseOptionsGroovyImpl@6a8bcb64>


    at com.xxx.mako.rrhh.persona.infrastructure.controller.PersonaRestAssuredSpec.findById(PersonaRestAssuredSpec.groovy:47)
Caused by: groovy.lang.MissingMethodException: No signature of method: io.restassured.internal.ContentParser.parse() is applicable for argument types: (io.restassured.internal.RestAssuredResponseImpl, io.restassured.internal.ResponseParserRegistrar...) values: [io.restassured.internal.RestAssuredResponseImpl@263e512e, io.restassured.internal.ResponseParserRegistrar@3f838072, ...]
Possible solutions: wait(), grep(), any()
    at io.restassured.internal.ResponseSpecificationImpl$HamcrestAssertionClosure.validate(ResponseSpecificationImpl.groovy:478)
    at io.restassured.internal.ResponseSpecificationImpl.validate(ResponseSpecificationImpl.groovy:92)
    at io.restassured.internal.ValidatableResponseOptionsImpl.spec(ValidatableResponseOptionsImpl.java:288)
    at io.restassured.internal.ValidatableResponseImpl.spec(ValidatableResponseImpl.groovy:76)
    ... 1 more

我在 gradle 中有这个依赖项:

    testRuntimeClasspath - Runtime classpath of compilation 'test' (target  (jvm)).
    +--- javax.inject:javax.inject:1
    +--- org.springdoc:springdoc-openapi-ui:1.3.9
    |    +--- org.springdoc:springdoc-openapi-webmvc-core:1.3.9
    |    |    +--- org.springdoc:springdoc-openapi-common:1.3.9
    |    |    |    +--- org.springframework.boot:spring-boot-autoconfigure:2.2.6.RELEASE -> 2.4.1
    |    |    |    |    \--- org.springframework.boot:spring-boot:2.4.1
    |    |    |    |         +--- org.springframework:spring-core:5.3.2
    |    |    |    |         |    \--- org.springframework:spring-jcl:5.3.2
    |    |    |    |         \--- org.springframework:spring-context:5.3.2
...
    |    |    +--- org.junit.jupiter:junit-jupiter-api:5.7.0
    |    |    |    +--- org.junit:junit-bom:5.7.0 (*)
    |    |    |    +--- org.apiguardian:apiguardian-api:1.1.0
    |    |    |    +--- org.opentest4j:opentest4j:1.2.0
    |    |    |    \--- org.junit.platform:junit-platform-commons:1.7.0
    |    |    |         +--- org.junit:junit-bom:5.7.0 (*)
    |    |    |         \--- org.apiguardian:apiguardian-api:1.1.0
    |    |    +--- org.junit.jupiter:junit-jupiter-params:5.7.0
    |    |    |    +--- org.junit:junit-bom:5.7.0 (*)
    |    |    |    +--- org.apiguardian:apiguardian-api:1.1.0
    |    |    |    \--- org.junit.jupiter:junit-jupiter-api:5.7.0 (*)
    |    |    \--- org.junit.jupiter:junit-jupiter-engine:5.7.0
    |    |         +--- org.junit:junit-bom:5.7.0 (*)
    |    |         +--- org.apiguardian:apiguardian-api:1.1.0
    |    |         +--- org.junit.platform:junit-platform-engine:1.7.0
    |    |         |    +--- org.junit:junit-bom:5.7.0 (*)
    |    |         |    +--- org.apiguardian:apiguardian-api:1.1.0
    |    |         |    +--- org.opentest4j:opentest4j:1.2.0
    |    |         |    \--- org.junit.platform:junit-platform-commons:1.7.0 (*)
    |    |         \--- org.junit.jupiter:junit-jupiter-api:5.7.0 (*)
    |    +--- org.mockito:mockito-core:3.6.28
    |    |    +--- net.bytebuddy:byte-buddy:1.10.18 -> 1.10.10
    |    |    +--- net.bytebuddy:byte-buddy-agent:1.10.18
    |    |    \--- org.objenesis:objenesis:3.1
    |    +--- org.mockito:mockito-junit-jupiter:3.6.28
    |    |    +--- org.mockito:mockito-core:3.6.28 (*)
    |    |    \--- org.junit.jupiter:junit-jupiter-api:5.4.2 -> 5.7.0 (*)
    |    +--- org.skyscreamer:jsonassert:1.5.0
    |    |    \--- com.vaadin.external.google:android-json:0.0.20131108.vaadin1
    |    +--- org.springframework:spring-core:5.3.2 (*)
    |    +--- org.springframework:spring-test:5.3.2
    |    |    \--- org.springframework:spring-core:5.3.2 (*)
    |    \--- org.xmlunit:xmlunit-core:2.7.0
    +--- junit:junit:4.11
    |    \--- org.hamcrest:hamcrest-core:1.3 -> 2.2
    |         \--- org.hamcrest:hamcrest:2.2
    +--- org.codehaus.groovy:groovy:3.0.7
    +--- org.codehaus.groovy:groovy-json3.0.7 FAILED
    +--- org.codehaus.groovy:groovy-xml3.0.7 FAILED
    +--- org.spockframework:spock-core:2.0-M4-groovy-3.0
    |    +--- org.junit:junit-bom:5.7.0 (*)
    |    +--- org.codehaus.groovy:groovy:3.0.6 -> 3.0.7
    |    +--- org.junit.platform:junit-platform-engine -> 1.7.0 (*)
    |    +--- org.junit.platform:junit-platform-testkit -> 1.7.0
    |    |    +--- org.junit:junit-bom:5.7.0 (*)
    |    |    +--- org.apiguardian:apiguardian-api:1.1.0
    |    |    +--- org.assertj:assertj-core:3.16.1 -> 3.18.1
    |    |    +--- org.opentest4j:opentest4j:1.2.0
    |    |    \--- org.junit.platform:junit-platform-launcher:1.7.0
    |    |         +--- org.junit:junit-bom:5.7.0 (*)
    |    |         +--- org.apiguardian:apiguardian-api:1.1.0
    |    |         \--- org.junit.platform:junit-platform-engine:1.7.0 (*)
    |    +--- org.hamcrest:hamcrest:2.2
    |    +--- org.jetbrains:annotations:19.0.0
    |    +--- org.ow2.asm:asm:9.0
    |    +--- net.bytebuddy:byte-buddy:1.10.16 -> 1.10.10
    |    +--- cglib:cglib-nodep:3.3.0
    |    \--- org.objenesis:objenesis:3.1
    +--- org.spockframework:spock-spring:2.0-M4-groovy-3.0
    |    +--- org.junit:junit-bom:5.7.0 (*)
    |    +--- org.codehaus.groovy:groovy:3.0.6 -> 3.0.7
    |    +--- org.spockframework:spock-core:2.0-M4-groovy-3.0 (*)
    |    +--- org.springframework:spring-test:4.3.5.RELEASE -> 5.3.2 (*)
    |    +--- org.springframework:spring-beans:4.3.5.RELEASE -> 5.3.2 (*)
    |    \--- org.springframework:spring-context:4.3.5.RELEASE -> 5.3.2 (*)
    +--- org.hamcrest:hamcrest-core:2.2 (*)
    +--- io.rest-assured:rest-assured:4.3.3
    |    +--- org.codehaus.groovy:groovy:3.0.7
    |    +--- org.codehaus.groovy:groovy-xml:3.0.7 -> 2.5.14
    |    |    \--- org.codehaus.groovy:groovy:2.5.14 -> 3.0.7
    |    +--- org.apache.httpcomponents:httpclient:4.5.3 -> 4.5.13
    |    |    +--- org.apache.httpcomponents:httpcore:4.4.13 -> 4.4.14
    |    |    \--- commons-codec:commons-codec:1.11 -> 1.15
    |    +--- org.apache.httpcomponents:httpmime:4.5.3 -> 4.5.13
    |    |    \--- org.apache.httpcomponents:httpclient:4.5.13 (*)
    |    +--- org.hamcrest:hamcrest:2.1 -> 2.2
    |    +--- org.ccil.cowan.tagsoup:tagsoup:1.2.1
    |    +--- io.rest-assured:json-path:4.3.3
    |    |    +--- org.codehaus.groovy:groovy-json:3.0.7 -> 2.5.14
    |    |    |    \--- org.codehaus.groovy:groovy:2.5.14 -> 3.0.7
    |    |    +--- org.codehaus.groovy:groovy:3.0.7
    |    |    \--- io.rest-assured:rest-assured-common:4.3.3
    |    |         +--- org.codehaus.groovy:groovy:3.0.7
    |    |         \--- org.apache.commons:commons-lang3:3.11
    |    \--- io.rest-assured:xml-path:4.3.3
    |         +--- org.codehaus.groovy:groovy-xml:3.0.7 -> 2.5.14 (*)
    |         +--- org.codehaus.groovy:groovy:3.0.7
    |         +--- io.rest-assured:rest-assured-common:4.3.3 (*)
    |         +--- org.apache.commons:commons-lang3:3.11
    |         +--- org.ccil.cowan.tagsoup:tagsoup:1.2.1
    |         +--- jakarta.xml.bind:jakarta.xml.bind-api:2.3.3 (*)
    |         \--- com.sun.xml.bind:jaxb-impl:2.3.3
    |              \--- jakarta.xml.bind:jakarta.xml.bind-api:2.3.3 (*)
    +--- io.rest-assured:rest-assured-common:4.3.3 (*)
    +--- io.rest-assured:json-path:4.3.3 (*)
    +--- io.rest-assured:xml-path:4.3.3 (*)
    +--- net.bytebuddy:byte-buddy:1.10.10
    \--- org.objenesis:objenesis:3.1

不明白为什么会出现这个版本:

   |         +--- org.codehaus.groovy:groovy-xml:3.0.7 -> 2.5.14 (*)
             +--- org.codehaus.groovy:groovy-json:3.0.7 -> 2.5.14

当我明确输入 3.0.7 时。我不明白最新版本的 Rest Assured 和 Groovy 3 之间的此类问题。 预先感谢您提供解决方案。

只是猜测,但您似乎忘记了 artifactIdversion 之间的 :

+--- org.codehaus.groovy:groovy-json3.0.7 FAILED
+--- org.codehaus.groovy:groovy-xml3.0.7 FAILED

org.codehaus.groovy:groovy-json3.0.7 应该是 org.codehaus.groovy:groovy-json:3.0.7.

没有 MCVE 这就是我从我的水晶球中收集到的东西。

是的,是我的错,我在 gradle 文件中省略了此依赖项中的“:”。

现在:

testImplementation("org.codehaus.groovy:groovy:${groovyVersion}")
testImplementation("org.codehaus.groovy:groovy-json:${groovyVersion}")
testImplementation("org.codehaus.groovy:groovy-xml:${groovyVersion}")
testImplementation("org.spockframework:spock-core:${groovySpockVersion}")
testImplementation("org.spockframework:spock-spring:${groovySpockVersion}")
// optional dependencies for using Spock

现在测试运行正常:

> Configure project :
executing dev liquibase
executing int liquibase
executing pre liquibase
executing pro liquibase

> Task :test

  com.xxx.mako.rrhh.persona.infrastructure.controller.PersonaRestAssuredSpec

    ? findById [idPersona: 1000, token: Basic ZHVtbXk6ZHVtbXk=, status: 401, specification: <io.restassured.internal.ResponseSpecificationImpl@30476d7a expectedStatusCode=null expectedStatusLine=null bodyMatchers=io.restassured.assertion.BodyMatcherGroup@4903a252 assertionClosure=io.restassured.internal.ResponseSpecificationImpl$HamcrestAssertionClosure@339460d8 headerAssertions=[] cookieAssertions=[] requestSpecification=io.restassured.internal.RequestSpecificationImpl@7f3c8b4b contentType=null restAssuredResponse=null bodyRootPath= rpr=io.restassured.internal.ResponseParserRegistrar@41c555c config=io.restassured.config.RestAssuredConfig@50d966bf response=null expectedResponseTime=null responseLogDetail=null forceDisableEagerAssert=false contentParser=null logRepository=io.restassured.internal.log.LogRepository@40db5edc>, #0] (11s)
    ? findById [idPersona: 2, token: Basic YWRtaW46YWRtaW4wMA==, status: 200, specification: <io.restassured.internal.ResponseSpecificationImpl@6dc2c46b expectedStatusCode=null expectedStatusLine=null bodyMatchers=io.restassured.assertion.BodyMatcherGroup@cd2d276 assertionClosure=io.restassured.internal.ResponseSpecificationImpl$HamcrestAssertionClosure@6f2591d5 headerAssertions=[] cookieAssertions=[] requestSpecification=io.restassured.internal.RequestSpecificationImpl@7d5a6a85 contentType=null restAssuredResponse=null bodyRootPath= rpr=io.restassured.internal.ResponseParserRegistrar@6c008d9 config=io.restassured.config.RestAssuredConfig@50d966bf response=null expectedResponseTime=null responseLogDetail=null forceDisableEagerAssert=false contentParser=null logRepository=io.restassured.internal.log.LogRepository@5ff3ba6a>, #1] (1.5s)
    ? findById (48.9s)

非常感谢@Leonard Brünings