前提条件
添加 rest assured 依赖包
<dependency>
<groupId>io.rest-assuredgroupId>
<artifactId>rest-assuredartifactId>
<version>4.4.0version>
dependency>
添加 json-schema-validator 依赖包
Rest Assured 自 2.1.0 版本起支持 JSON Schema validatation。为了使用这个功能,我们需要添加 “json-schema-validator” Java 库。
<dependency>
<groupId>io.rest-assuredgroupId>
<artifactId>json-schema-validatorartifactId>
<version>4.3.0version>
dependency>
当你在 Maven central repo 上搜索 json-schema-validator 时,会出现许多相同名字的库。确保 groupId 是 io.rest-assured。
JsonSchemaValidator 类提供了许多重载的静态的方法来执行 JSON schema 校验。
public static JsonSchemaValidator matchesJsonSchemaInClasspath(String pathToSchemaInClasspath) – Creates a Hamcrest matcher that validates that a JSON document conforms to the JSON schema provided to this method.
public static JsonSchemaValidator matchesJsonSchema(File file) – Creates a Hamcrest matcher that validates that a JSON document conforms to the JSON schema provided to this method.
如果将 JSON Schema 文件保存在项目的 resource folder 或 src/test/resources maven 项目,就可以直接用 matchesJsonSchemaInClasspath() 方法,只要传递 JSON Schema 文件名。如果 JSON Schema 文件存储在项目的不同地方或外部项目中,可以用 matchesJsonSchema() 方法。
Create JSON Schema
我们用到前一篇文章中相同的 JSON Schema,这个 JSON Schema 是基于 Restful Booker – Auth API 生成的。把这个 JSON Schema 存放在 src/test/resources。
AuthJsonSchema.json
{
"$schema": "http://json-schema.org/draft-07/schema",
"$id": "http://example.com/example.json",
"type": "object",
"title": "The root schema",
"description": "The root schema comprises the entire JSON document.",
"default": {},
"examples": [
{
"token": "abc123"
}
],
"required": [
"token"
],
"properties": {
"token": {
"$id": "#/properties/token",
"type": "string",
"title": "The token schema",
"description": "An explanation about the purpose of this instance.",
"default": "",
"examples": [
"abc123"
]
}
},
"additionalProperties": true
}
我们也可以直接在 body(Matcher matcher) 来调用 schema validator 方法。你可能会想到 JSON Schema validator 方法的返回类型是 JsonSchemaValidator, 怎么可以在 body(Matcher matcher) 方法调用呢。这是因为多层继承。 JsonSchemaValidator 类是间接实现 Matcher 接口。
import org.hamcrest.Matchers;
import org.junit.Test;
import io.restassured.RestAssured;
import io.restassured.http.ContentType;
import io.restassured.module.jsv.JsonSchemaValidator;
public class VerifyJsonSchema {
@Test
public void verifyJsonSchema() {
String jsonStringPayload = "{\"username\" : \"admin\",\"password\" : \"password123\"}";
// GIVEN
RestAssured
.given()
.baseUri("https://restful-booker.herokuapp.com/auth")
.contentType(ContentType.JSON)
.body(jsonStringPayload)
// WHEN
.when()
.post()
// THEN
.then()
.assertThat()
.statusCode(200)
.body("token", Matchers.notNullValue())
.body(JsonSchemaValidator.matchesJsonSchemaInClasspath("AuthJsonSchema.json"));
}
}
上面的测试会成功,做一些改动让期望的 JSON Schema 验证失败。
Adding extra required properties
上面 response 里只返回一个属性 “token”,我们在加一个新的属性 “nonExistingProperty” 在 JSON Schema “required” 部分:
{
"$schema": "http://json-schema.org/draft-07/schema",
"$id": "http://example.com/example.json",
"type": "object",
"title": "The root schema",
"description": "The root schema comprises the entire JSON document.",
"default": {},
"examples": [
{
"token": "abc123"
}
],
"required": [
"token",
"nonExistingProperty"
],
"properties": {
"token": {
"$id": "#/properties/token",
"type": "string",
"title": "The token schema",
"description": "An explanation about the purpose of this instance.",
"default": "",
"examples": [
"abc123"
]
}
},
"additionalProperties": true
}
失败
从 log 里可以看出一个必要的字段 “nonExistingProperty” 缺失了。
required: ["nonExistingProperty","token"] missing: ["nonExistingProperty"]
java.lang.AssertionError: 1 expectation failed.
Response body doesn't match expectation.
Expected: The content to match the given JSON schema.
warning: the following keywords are unknown and will be ignored: [$id, examples]
level: "warning"
schema: {"loadingURI":"file:/C:/ForKelly/Automation/RestAssuredDemo/target/test-classes/AuthJsonSchema.json#","pointer":""}
domain: "syntax"
ignored: ["$id","examples"]
warning: the following keywords are unknown and will be ignored: [$id, examples]
level: "warning"
schema: {"loadingURI":"file:/C:/ForKelly/Automation/RestAssuredDemo/target/test-classes/AuthJsonSchema.json#","pointer":"/properties/token"}
domain: "syntax"
ignored: ["$id","examples"]
error: object has missing required properties (["nonExistingProperty"])
level: "error"
schema: {"loadingURI":"file:/C:/ForKelly/Automation/RestAssuredDemo/target/test-classes/AuthJsonSchema.json#","pointer":""}
instance: {"pointer":""}
domain: "validation"
keyword: "required"
required: ["nonExistingProperty","token"]
missing: ["nonExistingProperty"]
Actual: [token:11b60187aad762d]
...............................
Change the data type of properties
属性 “token” 字段的值类型是 string,我们把它改成 integer 类型 “type”: “integer”,看看校验结果。
{
"$schema": "http://json-schema.org/draft-07/schema",
"$id": "http://example.com/example.json",
"type": "object",
"title": "The root schema",
"description": "The root schema comprises the entire JSON document.",
"default": {},
"examples": [
{
"token": "abc123"
}
],
"required": [
"token"
],
"properties": {
"token": {
"$id": "#/properties/token",
"type": "integer",
"title": "The token schema",
"description": "An explanation about the purpose of this instance.",
"default": "",
"examples": [
"abc123"
]
}
},
"additionalProperties": true
}
ditionalProperties": true
}
类型匹配失败
error: instance type (string) does not match any allowed primitive type (allowed: ["integer"])
java.lang.AssertionError: 1 expectation failed.
Response body doesn't match expectation.
Expected: The content to match the given JSON schema.
warning: the following keywords are unknown and will be ignored: [$id, examples]
level: "warning"
schema: {"loadingURI":"file:/C:/ForKelly/Automation/RestAssuredDemo/target/test-classes/AuthJsonSchema.json#","pointer":""}
domain: "syntax"
ignored: ["$id","examples"]
warning: the following keywords are unknown and will be ignored: [$id, examples]
level: "warning"
schema: {"loadingURI":"file:/C:/ForKelly/Automation/RestAssuredDemo/target/test-classes/AuthJsonSchema.json#","pointer":"/properties/token"}
domain: "syntax"
ignored: ["$id","examples"]
warning: the following keywords are unknown and will be ignored: [$id, examples]
level: "warning"
schema: {"loadingURI":"file:/C:/ForKelly/Automation/RestAssuredDemo/target/test-classes/AuthJsonSchema.json#","pointer":"/properties/token"}
domain: "syntax"
ignored: ["$id","examples"]
error: instance type (string) does not match any allowed primitive type (allowed: ["integer"])
level: "error"
schema: {"loadingURI":"file:/C:/ForKelly/Automation/RestAssuredDemo/target/test-classes/AuthJsonSchema.json#","pointer":"/properties/token"}
instance: {"pointer":"/token"}
domain: "validation"
keyword: "type"
found: "string"
expected: ["integer"]
Actual: [token:88a742f48f20b7c]
上面的例子中我们将 JSON Schema 文件放在 src/test/resources folder 下,可以使用 matchesJsonSchemaInClasspath() 方法。如果我们将 JSON Schema 文件不是放在 resource 文件夹下,那么就不能使用 matchesJsonSchemaInClasspath() 这个方法,否则会抛出 IllegalArgumentException 异常。
注意:
前面提到 schema 文件放在 src/test/resources folder 下,其实你也可以将 schema 文件放在 src/main/resources folder 下。测试类可以放在 src/main/java 或 src/test/java,但是作为好的实践标准,我们最好将测试相关的类放在 src/test/java folder 下。最好在 pom.xml 添加依赖包时移除 tag,这样就能避免不必要的范围限制。
例如:
<dependency>
<groupId>io.rest-assuredgroupId>
<artifactId>json-schema-validatorartifactId>
<version>4.3.0version>
<scope>testscope>
dependency>
移除 tag
<dependency>
<groupId>io.rest-assuredgroupId>
<artifactId>json-schema-validatorartifactId>
<version>4.3.0version>
dependency>
如果将 schema 文件不是放在 resource folder 下,我们就要用到另外一个静态方法 matchesJsonSchema() ,需要传一个完整的 JSON Schema 文件路径。
import java.io.File;
import org.hamcrest.Matchers;
import org.junit.Test;
import io.restassured.RestAssured;
import io.restassured.http.ContentType;
import io.restassured.module.jsv.JsonSchemaValidator;
public class VerifyJsonSchemaNonResource {
@Test
public void verifyJsonSchema() {
String jsonStringPayload = "{\"username\" : \"admin\",\"password\" : \"password123\"}";
// GIVEN
RestAssured
.given()
.baseUri("https://restful-booker.herokuapp.com/auth")
.contentType(ContentType.JSON)
.body(jsonStringPayload)
// WHEN
.when()
.post()
// THEN
.then()
.assertThat()
.statusCode(200)
.body("token", Matchers.notNullValue())
.body(JsonSchemaValidator.matchesJsonSchema(new File("C:\\Users\\kkk\\git\\master\\src\\test\\java\\JsonSchema\\schema.json")));
}
}
记住:
matchesJsonSchema 方法的参数是一个 File 对象,不是文件的完整路径 string 类型。matchesJsonSchema() 期望的 JSON schema 是一个 string 而不是一个 文件路径的 string.