RestAssured 中的无效 JSON Schema 异常,同时针对来自 swagger 的模式进行验证

Posted

技术标签:

【中文标题】RestAssured 中的无效 JSON Schema 异常,同时针对来自 swagger 的模式进行验证【英文标题】:invalid JSON Schema exception in RestAssured, while validating against schema from swagger 【发布时间】:2017-12-18 10:50:56 【问题描述】:

我正在使用 RestAssured 编写 TestNG 测试来测试 REST 服务。我从 Swagger 页面复制了响应模式并使用此模式验证响应。当我使用模式手动验证响应时,很好。当我从脚本中验证相同内容时,它会记录一个异常。

我的代码:

given().headers("X-Correlation-Id",correlationId)
 .get(url).then().assertThat().body(matchesJsonSchemaInClasspath("OffersByDivStoreSchema.json"));

JSON 架构:

[
  
    "couponAmount": 0,
    "couponAmt": 0,
    "couponDescription": "string",
    "couponNumber": "string",
    "creationDate": "string",
    "displayStartDate": "string",
    "divNumber": "string",
    "divStoreCoupon": "string",
    "expirationDate": "string",
    "maxSavings": 0,
    "minPurchaseAmount": 0,
    "minPurchaseQty": 0,
    "offerType": "string",
    "promoDescription": "string",
    "promoType": "string",
    "rewardYN": true,
    "updateDate": "string"
  
]

实际反应:

[

"divNumber": "999-00001",
"couponNumber": "5",
"divStoreCoupon": "999-00001-5",
"displayStartDate": "2017-11-17",
"expirationDate": "2017-10-21",
"updateDate": "2017-12-02",
"creationDate": "2018-01-12",
"couponAmount": 0.36735077591857157,
"minPurchaseAmount": -2026457608,
"minPurchaseQty": -767686725,
"maxSavings": 0.14174878169207628,
"couponDescription": "This is a Test",
"offerType": "MD",
"rewardYN": true,
"promoDescription": "This is a Test",
"promoType": "This is a Test",
"couponAmt": 0.9053900149839325
,

"divNumber": "999-00001",
"couponNumber": "2",
"divStoreCoupon": "999-00001-2",
"displayStartDate": "2018-01-09",
"expirationDate": "2017-08-13",
"updateDate": "2017-08-11",
"creationDate": "2017-12-05",
"couponAmount": 0.21459988049947543,
"minPurchaseAmount": -905064853,
"minPurchaseQty": 1016266945,
"maxSavings": 0.6997004570836415,
"couponDescription": "This is a Test",
"offerType": "MD",
"rewardYN": true,
"promoDescription": "This is a Test",
"promoType": "This is a Test",
"couponAmt": 0.36784946303275545

]

我的导入包括:

import static io.restassured.RestAssured.expect;
import static io.restassured.RestAssured.given;
import static io.restassured.module.jsv.JsonSchemaValidator.matchesJsonSchemaInClasspath;

这是我的例外:

io.restassured.module.jsv.JsonSchemaValidationException: com.github.fge.jsonschema.core.exceptions.InvalidSchemaException: fatal: invalid JSON Schema, cannot continue
Syntax errors:
[ 
  "level" : "error",
  "schema" : 
    "loadingURI" : "file:/C:/SOA_Scripts/spel-tests-offers/build/resources/test/OffersByDivStoreSchema.json#",
    "pointer" : ""
  ,
  "domain" : "syntax",
  "message" : "JSON value is of type array, not a JSON Schema (expected an object)",
  "found" : "array"
 ]
    level: "fatal"


    at io.restassured.module.jsv.JsonSchemaValidator.matchesSafely(JsonSchemaValidator.java:233)
    at io.restassured.module.jsv.JsonSchemaValidator.matchesSafely(JsonSchemaValidator.java:75)
    at org.hamcrest.TypeSafeMatcher.matches(TypeSafeMatcher.java:65)
    at org.hamcrest.Matcher$matches.call(Unknown Source)
    at org.codehaus.groovy.runtime.callsite.CallSiteArray.defaultCall(CallSiteArray.java:48)
    at org.codehaus.groovy.runtime.callsite.AbstractCallSite.call(AbstractCallSite.java:113)
    at org.codehaus.groovy.runtime.callsite.AbstractCallSite.call(AbstractCallSite.java:125)
    at io.restassured.assertion.BodyMatcher.validate(BodyMatcher.groovy:76)
    at io.restassured.assertion.BodyMatcher$validate$0.call(Unknown Source)
    at org.codehaus.groovy.runtime.callsite.CallSiteArray.defaultCall(CallSiteArray.java:48)
    at org.codehaus.groovy.runtime.callsite.AbstractCallSite.call(AbstractCallSite.java:113)
    at org.codehaus.groovy.runtime.callsite.AbstractCallSite.call(AbstractCallSite.java:141)
    at io.restassured.assertion.BodyMatcherGroup$_validate_closure2.doCall(BodyMatcherGroup.groovy:47)
    at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
    at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
    at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
    at java.lang.reflect.Method.invoke(Method.java:498)
    at org.codehaus.groovy.reflection.CachedMethod.invoke(CachedMethod.java:93)
    at groovy.lang.MetaMethod.doMethodInvoke(MetaMethod.java:325)
    at org.codehaus.groovy.runtime.metaclass.ClosureMetaClass.invokeMethod(ClosureMetaClass.java:294)
    at groovy.lang.MetaClassImpl.invokeMethod(MetaClassImpl.java:1027)
    at groovy.lang.Closure.call(Closure.java:414)
    at groovy.lang.Closure.call(Closure.java:430)
    at org.codehaus.groovy.runtime.DefaultGroovyMethods.collect(DefaultGroovyMethods.java:3170)
    at org.codehaus.groovy.runtime.DefaultGroovyMethods.collect(DefaultGroovyMethods.java:3140)
    at org.codehaus.groovy.runtime.dgm$66.invoke(Unknown Source)
    at org.codehaus.groovy.runtime.callsite.PojoMetaMethodSite$PojoMetaMethodSiteNoUnwrapNoCoerce.invoke(PojoMetaMethodSite.java:274)
    at org.codehaus.groovy.runtime.callsite.PojoMetaMethodSite.call(PojoMetaMethodSite.java:56)
    at org.codehaus.groovy.runtime.callsite.CallSiteArray.defaultCall(CallSiteArray.java:48)
    at org.codehaus.groovy.runtime.callsite.AbstractCallSite.call(AbstractCallSite.java:113)
    at org.codehaus.groovy.runtime.callsite.AbstractCallSite.call(AbstractCallSite.java:125)
    at io.restassured.assertion.BodyMatcherGroup.validate(BodyMatcherGroup.groovy:47)
    at io.restassured.assertion.BodyMatcherGroup$validate$3.call(Unknown Source)
    at org.codehaus.groovy.runtime.callsite.CallSiteArray.defaultCall(CallSiteArray.java:48)
    at org.codehaus.groovy.runtime.callsite.AbstractCallSite.call(AbstractCallSite.java:113)
    at org.codehaus.groovy.runtime.callsite.AbstractCallSite.call(AbstractCallSite.java:141)
    at io.restassured.internal.ResponseSpecificationImpl$HamcrestAssertionClosure.validate(ResponseSpecificationImpl.groovy:458)
    at io.restassured.internal.ResponseSpecificationImpl$HamcrestAssertionClosure$validate$1.call(Unknown Source)
    at org.codehaus.groovy.runtime.callsite.CallSiteArray.defaultCall(CallSiteArray.java:48)
    at org.codehaus.groovy.runtime.callsite.AbstractCallSite.call(AbstractCallSite.java:113)
    at org.codehaus.groovy.runtime.callsite.AbstractCallSite.call(AbstractCallSite.java:125)
    at io.restassured.internal.ResponseSpecificationImpl.validateResponseIfRequired(ResponseSpecificationImpl.groovy:643)
    at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
    at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
    at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
    at java.lang.reflect.Method.invoke(Method.java:498)
    at org.codehaus.groovy.runtime.callsite.PogoMetaMethodSite$PogoCachedMethodSiteNoUnwrapNoCoerce.invoke(PogoMetaMethodSite.java:210)
    at org.codehaus.groovy.runtime.callsite.PogoMetaMethodSite.callCurrent(PogoMetaMethodSite.java:59)
    at org.codehaus.groovy.runtime.callsite.CallSiteArray.defaultCallCurrent(CallSiteArray.java:52)
    at org.codehaus.groovy.runtime.callsite.AbstractCallSite.callCurrent(AbstractCallSite.java:154)
    at org.codehaus.groovy.runtime.callsite.AbstractCallSite.callCurrent(AbstractCallSite.java:166)
    at io.restassured.internal.ResponseSpecificationImpl.content(ResponseSpecificationImpl.groovy:94)
    at io.restassured.specification.ResponseSpecification$content$0.callCurrent(Unknown Source)
    at org.codehaus.groovy.runtime.callsite.CallSiteArray.defaultCallCurrent(CallSiteArray.java:52)
    at org.codehaus.groovy.runtime.callsite.AbstractCallSite.callCurrent(AbstractCallSite.java:154)
    at org.codehaus.groovy.runtime.callsite.AbstractCallSite.callCurrent(AbstractCallSite.java:174)
    at io.restassured.internal.ResponseSpecificationImpl.body(ResponseSpecificationImpl.groovy:244)
    at io.restassured.internal.ValidatableResponseOptionsImpl.body(ValidatableResponseOptionsImpl.java:262)
    at com.kroger.spel.services.OffersByDivStore.validateOffersForStore(OffersByDivStore.java:80)
    at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
    at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
    at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
    at java.lang.reflect.Method.invoke(Method.java:498)
    at org.testng.internal.MethodInvocationHelper.invokeMethod(MethodInvocationHelper.java:108)
    at org.testng.internal.Invoker.invokeMethod(Invoker.java:661)
    at org.testng.internal.Invoker.invokeTestMethod(Invoker.java:869)
    at org.testng.internal.Invoker.invokeTestMethods(Invoker.java:1193)
    at org.testng.internal.TestMethodWorker.invokeTestMethods(TestMethodWorker.java:126)
    at org.testng.internal.TestMethodWorker.run(TestMethodWorker.java:109)
    at org.testng.TestRunner.privateRun(TestRunner.java:744)
    at org.testng.TestRunner.run(TestRunner.java:602)
    at org.testng.SuiteRunner.runTest(SuiteRunner.java:380)
    at org.testng.SuiteRunner.runSequentially(SuiteRunner.java:375)
    at org.testng.SuiteRunner.privateRun(SuiteRunner.java:340)
    at org.testng.SuiteRunner.run(SuiteRunner.java:289)
    at org.testng.SuiteRunnerWorker.runSuite(SuiteRunnerWorker.java:52)
    at org.testng.SuiteRunnerWorker.run(SuiteRunnerWorker.java:86)
    at org.testng.TestNG.runSuitesSequentially(TestNG.java:1301)
    at org.testng.TestNG.runSuitesLocally(TestNG.java:1226)
    at org.testng.TestNG.runSuites(TestNG.java:1144)
    at org.testng.TestNG.run(TestNG.java:1115)
    at org.testng.IDEARemoteTestNG.run(IDEARemoteTestNG.java:72)
    at org.testng.RemoteTestNGStarter.main(RemoteTestNGStarter.java:127)
Caused by: com.github.fge.jsonschema.core.exceptions.InvalidSchemaException: fatal: invalid JSON Schema, cannot continue

有人可以建议这里出了什么问题。

【问题讨论】:

有什么例外? 刚刚注意到在我的帖子中错过了它。现在添加了异常:( 错误已经解决here,遗憾的是,答案没有提供任何解释,但也许您可以找出挥之不去的问题? 谢谢@Nathan 我找到了原因。我使用来自 swagger-ui 的模型模式来验证,而不是来自 swagger 的 v2/api-docs.json 的实际模式。当我更改架构时,相同的代码起作用了。 很高兴它现在可以工作,如果您找到了确切的原因,您可以发布您的解决方案作为答案并接受它:) 【参考方案1】:

问题是我从 swagger-ui.html 页面上的模型架构复制了架构。但是,请注意,这是响应的表示。实际架构是在 /v2/api-docs.json 页面上创建的。不得不做一些工作,因为有很多定义被重用,并且必须将它们全部放在每个端点的一个模式文件中。下面是我的最终架构文档。


  "type": "array",
  "items": 
    "type": "object",
    "properties": 
      "couponAmount": 
        "type": "number",
        "format": "double"
      ,
      "couponAmt": 
        "type": "number",
        "format": "double"
      ,
      "couponDescription": 
        "type": "string"
      ,
      "couponNumber": 
        "type": "string"
      ,
      "creationDate": 
        "type": "string"
      ,
      "displayStartDate": 
        "type": "string"
      ,
      "divNumber": 
        "type": "string"
      ,
      "divStoreCoupon": 
        "type": "string"
      ,
      "expirationDate": 
        "type": "string"
      ,
      "maxSavings": 
        "type": "number",
        "format": "double"
      ,
      "minPurchaseAmount": 
        "type": "integer",
        "format": "int32"
      ,
      "minPurchaseQty": 
        "type": "integer",
        "format": "int32"
      ,
      "offerType": 
        "type": "string"
      ,
      "promoDescription": 
        "type": "string"
      ,
      "promoType": 
        "type": "string"
      ,
      "rewardYN": 
        "type": "boolean"
      ,
      "updateDate": 
        "type": "string"
      
    
  

更新架构后,相同的代码工作。谢谢Nathan,您的参考帮助我解决了这个问题。

【讨论】:

以上是关于RestAssured 中的无效 JSON Schema 异常,同时针对来自 swagger 的模式进行验证的主要内容,如果未能解决你的问题,请参考以下文章

请放心 - 无效的 JSON 表达式:Script1.groovy:1:意外输入:'['

RestAssured API测试框架中的否定断言或否定匹配[关闭]

线程“ main”中的异常java.lang.NoClassDefFoundError:io / restassured / RestAssured

RestAssured 不尊重 Quarkus 中的 ObjectMapper 配置

导入 io.restassured.RestAssured 无法解析

在 RestAssured jsonPathEvaluator 中,没有为双值提供正确的值