The original version

Jane: www.jianshu.com/p/6bfaca87a… Blog garden: www.cnblogs.com/zy7y/p/1342… Testerhome:testerhome.com/topics/2500…

Latest use case screenshots and use case filling formats

Data dependency/path parameter dependency

For example, some query interfaces need to be logged in before they can be operated, so we need to get tokens or something like that. This part of things is put in the header. ApiAutoTest only revolves around path parameter dependence and request data dependence

  • Path parameter dependence

    For example, restful, a Users interface, routes to users like this, and its request is get. This route is considered to look up all users. If it looks up a user like this, users/: ID is also a GET request. There is a parameter that requires a userId, such as any one from 1 to 500, that is, the id is variable and can be given a value called userId from the response returned by the login interface

  • Request parameter dependency

    The order ID required by the payment interface is the response order ID returned by the submit order interface from the previous step

For example

Suppose you now have a dictionary of actual response results as follows

{"case_002": {
        "data": {
            "id": 500."username": "admin"."mobile": "12345678",}},"case_005": {
        "data": {
            "id": 511."create_time": 1605711095}}},Copy the code
  • The interface path content in Excel is as follows: Users /&$.case_005.data.id&/state/&$.case_005.data.careate_time&

    Code inside resolved as follows: the users / 511 / state / 1605711095

    &$.case_005.data.id& represents the value of the ID in the data dictionary of case_005 extracted from the response dictionary. The result is 511

  • The request parameters in Excel are as follows:

    {
     "pagenum": 1."pagesize": "12"."data": &$.case_005.data&, 
     "userId": &$.case_002.data.id&
    }
    Copy the code

    The code internally parses as follows:

    {
     "pagenum": 1."pagesize": "12"."data": {
                "id": 511."create_time": 1605711095
            }, 
     "userId": 500
    }
    Copy the code

It’s not hard to see the rules& jsonPath extract syntax &If you want something to be a string, just do this"&jsonPath extract syntax &"

Upload a file

Write the format in the use case in the upload file bar

# Single file upload in Excel
{"Parameter name of the file object accepted in the interface": "File path address"}

# Multiple file upload in Excel writing method
{"Parameter name of the file object accepted in the interface": ["File path 1"."File path 2"]}
Copy the code

The expected results

Use case writing format

# assert a content
{"Jsonpath extraction expression": Expected result content}# multiple assertions
{"Jsonpath extract expression 1": Expected result1."Jsonpath extract expression 2": Expected result2}
Copy the code

Other optimization

  • The config.yaml file added configurable initial header, the overall code optimization, compared with the previous test case execution, about 2s faster
  • Integrate configuration file reading, use case reading inread_file.pyUnder the
  • Remove the report compression method
  • Reducing Log Information

Now rely on the processing code

tools/init.py

#! /usr/bin/env/python3
# -*- coding:utf-8 -*-
"""
@project: apiAutoTest
@author: zy7y
@file: __init__.py
@ide: PyCharm
@time: 2020/7/31
"""
import json
import re

import allure

from jsonpath import jsonpath
from loguru import logger


def extractor(obj: dict, expr: str = '. ') - >object:
    $. Case; $. Case; $. Case; $.case.data :param obj :json/dict data :param expr: expression,. $.data = data; $.data = data; $.data = data;
    try:
        result = jsonpath(obj, expr)[0]
    except Exception as e:
        logger.error(F 'could not extract the content, throwing you an error!{e}')
        result = None
    return result


def rep_expr(content: str, data: dict, expr: str = '& (. *?) & ') - >str:
    """ From the string of request parameters, use the regular method to find the appropriate string content and replace :param content: the original string content: param data: in this project is generally the response dictionary, from the dictionary value :param expr: Return content: Replace the string """
    for ctt in re.findall(expr, content):
        content = content.replace(f'&{ctt}& '.str(extractor(data, ctt)))
    return content


def convert_json(dict_str: str) - >dict:
    """ :param dict_str: dictionary-like string return json format content """
    try:
        if 'None' in dict_str:
            dict_str = dict_str.replace('None'.'null')
        elif 'True' in dict_str:
            dict_str = dict_str.replace('True'.'true')
        elif 'False' in dict_str:
            dict_str = dict_str.replace('False'.'false')
        dict_str = json.loads(dict_str)
    except Exception as e:
        if 'null' in dict_str:
            dict_str = dict_str.replace('null'.'None')
        elif 'true' in dict_str:
            dict_str = dict_str.replace('true'.'True')
        elif 'False' in dict_str:
            dict_str = dict_str.replace('false'.'False')
        dict_str = eval(dict_str)
        logger.error(e)
    return dict_str


def allure_title(title: str) - >None:
    """ Use-case title shown in Allure """
    allure.dynamic.title(title)


def allure_step(step: str, var: str) - >None:
    "" :param step: step and attachment name :param var: attachment content ""
    with allure.step(step):
        allure.attach(json.dumps(var, ensure_ascii=False, indent=4), step, allure.attachment_type.TEXT)
Copy the code

tools/data_process.py

#! /usr/bin/env/python3
# -*- coding:utf-8 -*-
"""
@project: apiAutoTest
@author: zy7y
@file: data_process.py
@ide: PyCharm
@time: 2020/11/18
"""
from tools import logger, extractor, convert_json, rep_expr, allure_step
from tools.read_file import ReadFile


class DataProcess:
    response_dict = {}
    header = ReadFile.read_config('$.request_headers')
    have_token = header.copy()

    @classmethod
    def save_response(cls, key: str, value: object) - >None:
        "" save the actual response :param key: saves the key in the dictionary, generally using the case number :param value: saves the value in the dictionary, using json response """
        cls.response_dict[key] = value
        logger.info(F 'to add the key:{key}, corresponding to the value:{value}')

    @classmethod
    def handle_path(cls, path_str: str) - >str:
        """ Path parameter handling :param path_str: /&$.case_005.data.id&/state/&$.case_005.data.create_time& Extract the value of id from the case_005 data dictionary from the response dictionary, let's say 500, Return /511/state/1605711095 """
        # /&$.case.data.id&/state/&$.case_005.data.create_time&
        return rep_expr(path_str, cls.response_dict)

    @classmethod
    def handle_header(cls, token: str) - >dict:
        """ Processing header :param Token: Write: write token to header, read: use header with token, empty: Use header without token return """
        if token == 'read':
            return cls.have_token
        else:
            return cls.header

    @classmethod
    def handler_files(cls, file_obj: str) - >object:
        """file object handling method :param file_obj: upload file used, format: interface file parameter name :" file path address "/[" file address 1", "file address 2"] instance - single file: &file&D: """
        if file_obj == ' ':
            return
        for k, v in convert_json(file_obj).items():
            # Multiple file uploads
            if isinstance(v, list):
                files = []
                for path in v:
                    files.append((k, (open(path, 'rb'))))
            else:
                # Single file upload
                files = {k: open(v, 'rb')}
        return files

    @classmethod
    def handle_data(cls, variable: str) - >dict:
        "" "the request data processing: param variable: the request data, the incoming is convertible dictionary/json string, which can contain variables expressions return after processing json/dict data dictionary ", ""
        if variable == ' ':
            return
        data = rep_expr(variable, cls.response_dict)
        variable = convert_json(data)
        return variable

    @classmethod
    def assert_result(cls, response: dict, expect_str: str) :
        """ Expected result Actual result Assert method :param response: actual response dictionary: Param expect_str: expected response content, read from excel return None """
        expect_dict = convert_json(expect_str)
        index = 0
        for k, v in expect_dict.items():
            actual = extractor(response, k)
            index += 1
            logger.info('the first f{index}Three assertions, actual results:{actual}| expected results:{v}\n Assert the result{actual == v}')
            allure_step('the first f{index}An assertion that '.F 'Actual results:{actual}= Expected results:{v}')
            assert actual == v
Copy the code

The source address

Master: The branch is the latest code

Version1.0: branching to previously open source code (dealing with data dependencies through dictionary iteration)

Gitee.com/zy7y/apiAut…

Github.com/zy7y/apiAut…

Subsequent to

At present, the company is doing interface testing. To be honest, it is also fumbling. The above optimization items are suddenly thought of during the actual process, and then updated

  • Some interfaces do not return the required data, so the result of the pre-sql query needs to be transmitted to the request data. The post-SQL is mainly used to check whether the data in the database has changed after the request, and the database assertion is completed 2020/12/08. There should be no major updates to apiAutoTest at this point ~~~ thanks for watching this Demo
  • Enterprise WeChat push: at present, the project expected effect, is the back-end personnel commit code, automated deployment, through gitlab – ci start test code, the interface test after completion of an abnormal test results in acquisition allure/failure cases will send E-mail and enterprise WeChat pushed to leadership This is to send the request to
  • . Don’t say there are many optimization items, ability is not enough to charge it, ~ ~

Thank you

Thank you for your help with apiAutoTest, thanks ~ ~ ~