• 接口测试进阶:在接口测试中框架中使用json schema


    当今接口测试越来越重要,一般情况下我们总是会对接口的返回的json字符串进行验证,看返回是否跟我们的预期相符。不过很多情况下我们会遇到下面的问题

    • 响应结果在测试中不停的发生变动,比如昨天还是3个字段,今天可能返回值里只有2个字段了,测试这边没有比较好的方式感受到后端的变化
    • 我们需要对json的返回值进行一些校验,需要写很多的断言,大部分时候这些断言都是相似的,或者是重复的,比如说校验某个字段的长度必须小于10之类的

    那如何解决呢?

    • 与前后端沟通好返回值的字段,类型以及校验规则,最好有前后端+测试端统一一份合约,大家都按照合约来进行数据的处理
    • 测试的时候通过合约里定义好的校验规则进行数据校验

    这时候json schema就派上用场了。

    json schema

    JSON Schema 是一种 JSON 媒体类型,用于定义 JSON 数据的结构。 JSON 模式旨在定义 JSON 数据的验证,可用于验证响应和请求 JSON。 在 JSON Schema 中,我们可以验证数据类型、字段是否为必填、最小长度或最大长度等。

    举例

    下面的数据代表了一个员工的信息

    • id: employeeId
    • 员工名称: employeeName
    • 年龄: employeeAge
    • 职称: jobTitle
    • 爱好: hobby
    {
      "employeeId": 1,
      "employeeName": "Fulan",
      "employeeAge": 23,
      "jobTitle": "SDET",
      "hobby": [
        "watch movies",
        "play football"
      ]
    }
    

    上面的定义其实是有一些疑问的,比如

    • id是什么意思
    • employeeName的最大长度是多少
    • employeeAge的最小值是什么
    • jobTitle是必填吗
    • hobby可以填几个

    我们可以通过生成JSON schema来回答上面的问题

    {
        "$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": [
            {
                "employeeId": 1,
                "employeeName": "Fulan",
                "employeeAge": 23,
                "jobTitle": "SDET",
                "hobby": [
                    "watch movie",
                    "play football"
                ]
            }
        ],
        "required": [
            "employeeId",
            "employeeName",
            "employeeAge",
            "jobTitle",
            "hobby"
        ],
        "properties": {
            "employeeId": {
                "$id": "#/properties/employeeId",
                "type": "integer",
                "title": "The employeeId schema",
                "description": "An explanation about the purpose of this instance.",
                "default": 0,
                "examples": [
                    1
                ]
            },
            "employeeName": {
                "$id": "#/properties/employeeName",
                "type": "string",
                "title": "The employeeName schema",
                "description": "An explanation about the purpose of this instance.",
                "default": "",
                "examples": [
                    "Fulan"
                ]
            },
            "employeeAge": {
                "$id": "#/properties/employeeAge",
                "type": "integer",
                "title": "The employeeAge schema",
                "description": "An explanation about the purpose of this instance.",
                "default": 0,
                "examples": [
                    23
                ]
            },
            "jobTitle": {
                "$id": "#/properties/jobTitle",
                "type": "string",
                "title": "The jobTitle schema",
                "description": "An explanation about the purpose of this instance.",
                "default": "",
                "examples": [
                    "SDET"
                ]
            },
            "hobby": {
                "$id": "#/properties/hobby",
                "type": "array",
                "title": "The hobby schema",
                "description": "An explanation about the purpose of this instance.",
                "default": [],
                "examples": [
                    [
                        "watch movies",
                        "play football"
                    ]
                ],
                "additionalItems": true,
                "items": {
                    "$id": "#/properties/hobby/items",
                    "anyOf": [
                        {
                            "$id": "#/properties/hobby/items/anyOf/0",
                            "type": "string",
                            "title": "The first anyOf schema",
                            "description": "An explanation about the purpose of this instance.",
                            "default": "",
                            "examples": [
                                "watch movies",
                                "play football"
                            ]
                        }
                    ]
                }
            }
        },
        "additionalProperties": true
    }
    

    看上去很多很乱对不对,别着急,我们慢慢看

    • $schema 关键字表明此模式是根据标准的特定草案编写的,并且用于各种原因,主要是版本控制。
    • $id 关键字定义模式的 URI 和模式中其他 URI 引用解析的基本 URI。
    • title和description注释关键字只是描述性的。 它们不会对正在验证的数据添加约束。 使用这两个关键字来说明模式的意图。
    • type 关键字定义了我们的 JSON 数据的第一个约束,在这种情况下,它必须是一个 JSON 对象。

    更具体一点

    properties里定义了各个字段的详情,我们可以在里面增加更多的约束

    {
        "$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": [
            {
                "employeeId": 1,
                "employeeName": "Fulan",
                "employeeAge": 23,
                "jobTitle": "SDET",
                "hobby": [
                    "watch movie",
                    "play football"
                ]
            }
        ],
        "required": [
            "employeeId",
            "employeeName",
            "employeeAge",
            "jobTitle",
            "hobby"
        ],
        "properties": {
            "employeeId": {
                "$id": "#/properties/employeeId",
                "type": "integer",
                "title": "The employeeId schema",
                "description": "An explanation about the purpose of this instance.",
                "default": 0,
                "examples": [
                    1
                ]
            },
            "employeeName": {
                "$id": "#/properties/employeeName",
                "type": "string",
                "title": "The employeeName schema",
                "description": "An explanation about the purpose of this instance.",
                "default": "",
                "examples": [
                    "Fulan"
                ]
            },
            "employeeAge": {
                "$id": "#/properties/employeeAge",
                "type": "integer",
                "title": "The employeeAge schema",
                "description": "An explanation about the purpose of this instance.",
                "default": 0,
                "exclusiveMinimum": 20,
                "examples": [
                    23
                ]
            },
            "jobTitle": {
                "$id": "#/properties/jobTitle",
                "type": "string",
                "title": "The jobTitle schema",
                "description": "An explanation about the purpose of this instance.",
                "default": "",
                "minLength": 4,
                "examples": [
                    "SDET"
                ]
            },
            "hobby": {
                "$id": "#/properties/hobby",
                "type": "array",
                "title": "The hobby schema",
                "description": "An explanation about the purpose of this instance.",
                "default": [],
                "examples": [
                    [
                        "watch movies",
                        "play football"
                    ]
                ],
                "additionalItems": true,
                "items": {
                    "$id": "#/properties/hobby/items",
                    "anyOf": [
                        {
                            "$id": "#/properties/hobby/items/anyOf/0",
                            "type": "string",
                            "title": "The first anyOf schema",
                            "description": "An explanation about the purpose of this instance.",
                            "default": "",
                            "examples": [
                                "watch movies",
                                "play football"
                            ]
                        }
                    ]
                },
                "uniqueItems": true
            }
        },
        "additionalProperties": true
    }
    

    在上面的例子中我们规定

    • employeeId的默认值是0
    • employeeAge最小值是20
    • jobTitle的最小长度是4
    • hobbies必须排重,所以uniqueItems的值是true

    json schema合约可以尽可能的详细,这样模糊的点就会相对较少,验证的结果会更加的准确。

    在测试框架中使用json schema

    这里以java为例,首先我们引入json schema的支持,然后定义断言工具,最后在用例中使用该断言。

    引入json schema支持,这里用的是https://github.com/everit-org/json-schema,pom.xml如下

    <?xml version="1.0" encoding="UTF-8"?>
    <project xmlns="http://maven.apache.org/POM/4.0.0"
             xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
             xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
        <modelVersion>4.0.0</modelVersion>
    
        <groupId>org.example</groupId>
        <artifactId>json-schema</artifactId>
        <version>1.0-SNAPSHOT</version>
    
        <properties>
            <maven.compiler.source>8</maven.compiler.source>
            <maven.compiler.target>8</maven.compiler.target>
        </properties>
        <dependencies>
            <dependency>
                <groupId>org.testng</groupId>
                <artifactId>testng</artifactId>
                <version>7.4.0</version>
                <scope>test</scope>
            </dependency>
            <dependency>
                <groupId>org.everit.json</groupId>
                <artifactId>org.everit.json.schema</artifactId>
                <version>1.3.0</version>
            </dependency>
            <dependency>
                <groupId>com.fasterxml.jackson.core</groupId>
                <artifactId>jackson-databind</artifactId>
                <version>2.12.4</version>
            </dependency>
            <dependency>
                <groupId>io.rest-assured</groupId>
                <artifactId>rest-assured</artifactId>
                <version>4.3.0</version>
                <scope>test</scope>
            </dependency>
        </dependencies>
    
    </project>
    view raw
    

    定义断言工具函数

    package utils;
    
    import com.fasterxml.jackson.databind.ObjectMapper;
    import functest.APITest;
    import org.everit.json.schema.Schema;
    import org.everit.json.schema.ValidationException;
    import org.everit.json.schema.loader.SchemaLoader;
    import org.json.JSONObject;
    import org.json.JSONTokener;
    import org.testng.Assert;
    
    import java.util.logging.Logger;
    
    public class JsonSchemaUtils {
    
        private static Logger LOGGER= Logger.getLogger(String.valueOf(JsonSchemaUtils.class));
    
        private ObjectMapper objMapper = new ObjectMapper();
    
        public void checkJsonSchema(String jsonSchemaPath, String jsonSubject) throws ValidationException {
    
            JSONObject retVal = new JSONObject();
    
            try {
                JSONObject jsonSchema = new JSONObject(new JSONTokener(APITest.class.getResourceAsStream(jsonSchemaPath)));
                Schema schema = SchemaLoader.load(jsonSchema);
                schema.validate(objMapper.convertValue(jsonSubject, JSONObject.class));
                retVal.put("errorMessage","");
            } catch (ValidationException ex) {
                ex.printStackTrace();
                LOGGER.info("JSON Schema Error Message: " + ex.getMessage());
                retVal.put("errorMessage",ex.getMessage());
                Assert.assertEquals(retVal.getString("errorMessage"), "");
            }
            Assert.assertEquals(retVal.getString("errorMessage"), "");
        }
    }
    

    在用例中使用

    package functest;
    
    import io.restassured.RestAssured;
    import org.json.JSONObject;
    import org.testng.annotations.Test;
    import io.restassured.response.Response;
    import utils.JsonSchemaUtils;
    
    public class APITest {
    
        private final static String JSON_SCHEMA_ACTIVITY_PATH = "/schema/apiTest-jsonSchema.response.json";
    
        JsonSchemaUtils jsonSchemaUtils = new JsonSchemaUtils();
    
        @Test
        public void getTest() {
    
            Response response = RestAssured.get("https://www.boredapi.com/api/activity/");
            JSONObject jsonObj = new JSONObject(response.getBody().asString());
            System.out.println(jsonObj.toString(4));
            jsonSchemaUtils.checkJsonSchema(JSON_SCHEMA_ACTIVITY_PATH,response.asString());
    
        }
    
    }
    

    最后

    JSON schema 是一个多功能库,可以帮助我们执行 API 测试,使用 JSON 文件定义 schema 要求的能力显示了这个库的强大功能。 希望这些示例能让您了解如何在项目中使用模式验证。

  • 相关阅读:
    Begin Example with Override Encoded SOAP XML Serialization
    State Machine Terminology
    How to: Specify an Alternate Element Name for an XML Stream
    How to: Publish Metadata for a WCF Service.(What is the Metadata Exchange Endpoint purpose.)
    Beginning Guide With Controlling XML Serialization Using Attributes(XmlSerializaiton of Array)
    Workflow 4.0 Hosting Extensions
    What can we do in the CacheMetaData Method of Activity
    How and Why to use the System.servicemodel.MessageParameterAttribute in WCF
    How to: Begin Sample with Serialization and Deserialization an Object
    A Test WCF Service without anything of config.
  • 原文地址:https://www.cnblogs.com/nbkhic/p/15838681.html
Copyright © 2020-2023  润新知