The foreword 0.

ApiAutoTest has been open source since Last August and has been updated a lot

  • First version

    Py - 2020/08/09 Implement multi-file upload, interface Path parameter dependency processing Initial implementation uses an iterative approach to deal with the data dependency relationship in the interfaceCopy the code
  • Version 2

    - 2020/11/18 uses re library to replace previous dictionary iteration to handle data dependencies - 2020/11/21 config.yaml file added base header Settings - 2020/11/22 supports dynamic field assertions when field assertions are expected in results - 2020/12/08 Optimized assertion information, added database (mysql) query operations, used '@pytest.fixture(scope="session")' to host database objects, New SQL column for use Cases - 2020/12/16 Using conftest.py to initialize use cases, added failure rerun mechanism, added run file, optimized redundant code for test_api.pyCopy the code
  • Third Edition

    - 2021/01/19 Add data cleaning function (database backup is performed before the test starts - backup is performed on the server and local respectively, and backup is performed to restore data after the test ends - attempt is made to restore data from the server and local to the server database. Mysql service deployed by Docker has been locally debugable. - 2021/02/27 Added hooks. Py file (you can customize methods here and use them in use cases, be sure to use return in defined methods), removed eval syntax sugar from last update, added logging before use case processingCopy the code

At one point, I said I would not update the maintenance code, but it is still slowly updated…

1. Custom function implementation story

This is updated today, and the main requirement comes from the feedback of a apiAutoTest learner. Here’s to thank him. Before this, another friend said that he needed to calculate the ID field returned by the previous interface

2. How to use custom functions in use cases

2.1 Define functions in Tools/links.py

def get_current_highest() :
    """ Get the current timestamp ""
    return int(time.time())


def sum_data(a, b) :
    """ Compute function """
    return a + b
Copy the code

2.2 How is this function used in use cases

Syntax sugar: @ function name ()@: use no-argument function

@ Function name (argument 1, argument 2)@: Passes arguments to the function

Ps: function arguments are compatible with the extract dependency syntax in apiAutoTest & jsonPath syntax here &

Use cases (for fear of unclear screenshots, two are simply simulated here)

Use case number Use case title The interface path Whether or not to perform Token operation Request way Input parameter keyword Upload a file The request data The rear SQL The expected results
case_001 Post requests to implement login login is write post data {“username”: “admin”, “password”: “123456”} select * from user where id=&$.case_002.data.id&; {“$. Meta “:{” MSG “: “login successful “, “status”: 200}}
case_002 The debug function sum_data(), from path, requires operations users/@sum_data(&$.case_001.data.id&, 2)@/ is read put data {“username”: “tery”,”password”: @sum_data(&$.case_001.data.id&, 66)@, “timer”: @get_current_highest()@, “timer_str”: ” @get_current_highest()@”} {“$. Meta “:{” MSG “: “set status”, “status”: 200}}

Run logs (running the above use cases results in logs as follows)

The 2021-02-27 16:06:50. 538 | DEBUG | API. Base_requests: send_request: 34 - use cases for processing data before: interface path: the login request parameters: {" username ": SQL: select * from user where id=&$.case_002.data.id&; Expected result :{"$.meta":{" MSG ": "login successful ", "status": 765 | 200}} 2021-02-27 16:06:50. INFO | API. Base_requests: send_api: 81 - Eventually request address: http://www.ysqorz.top:8888/api/private/v1/login request method: post request: {' Accept - Encoding: 'gzip, deflate', 'Accept-Language': 'zh-CN,zh; Q =0.9', 'user-agent ': 'Mozilla/5.0 (Windows NT 10.0; Win64; X64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/86.0.4240.198 Safari/537.36'} '123456'} to upload files: None response data: {" data ": {" id" : 500, 'rid' : 0, 'username' : 'admin', 'mobile', '12345678', 'email: '[email protected]', 'token': 'Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1aWQiOjUwMCwicmlkIjowLCJpYXQiOjE2MTQ0MTMyMTAsImV4cCI6MTYxNDQ5OTYxMH0.cZTYLARKNj8 SKlPGPdIUh9RmyQaYAJnJrLObaKiNiU4 meta ': {'},' 'MSG' : 'login successfully,' status' : 200}} 2021-02-27 16:06:50. 773 | INFO | view data_process: save_response: 27 - add key: case_001, corresponding value: {" data ": {" id" : 500, 'rid': 0, 'username': 'admin', 'mobile': '12345678', 'email': '[email protected]', 'token': 'Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1aWQiOjUwMCwicmlkIjowLCJpYXQiOjE2MTQ0MTMyMTAsImV4cCI6MTYxNDQ5OTYxMH0.cZTYLARKNj8 SKlPGPdIUh9RmyQaYAJnJrLObaKiNiU4 meta ': {'},' 'MSG' : 'login successfully,' status' : 775 | 200}} 2021-02-27 16:06:50. INFO | view data_process: assert_result: 115 - the first assertion, the actual result: {' MSG ':' login successfully, 'status' : 200} | expected results: {' MSG ':' login successful ', 'status' : 200} assertion results True 16:06:50 2021-02-27. 775 | DEBUG | API. Base_requests: send_request: 34 - use cases for processing data before: interface path: The users / @ sum_data (& $. Case_001. Data. The id &, 2) @ / request parameters: {" username ":" tery ", "password" : @ sum_data (& $. Case_001. Data. Id &, 66) @, "timer" : @ get_current_highest () @, "timer_str" : "@ get_current_highest () @" rear} SQL: Expected result :{" $.meta":{" MSG ": "set status successful ", "status": 200}} 2021-02-27 16:06:50. 775 | DEBUG | tools: rep_expr: 45 - & $. Case_001. Data. The id & replace the value for the 500 2021-02-27 16:06:50. 775 | DEBUG | tools: rep_expr: 50 - perform sum_data hooks functions (500, 2) replacement value is 502 16:06:50. 2021-02-27 775 | DEBUG | tools: rep_expr: 45 - & $. Case_001. Data. The id & replacement value is 500 2021-02-27 16:06:50. 783 | DEBUG | tools: rep_expr: 50 - perform sum_data hooks functions (500, 66) to replace the value of 566 16:06:50. 2021-02-27 783 | DEBUG | tools: rep_expr: 50 - perform get_current_highest hooks functions () replacement value is 1614413210 The 2021-02-27 16:06:50. 783 | DEBUG | tools: rep_expr: 50 - perform hooks function get_current_highest () replacement value is 1614413210 2021-02-27 16:06:50. 835 | INFO | API. Base_requests: send_api: 81 - the final request address: http://www.ysqorz.top:8888/api/private/v1/users/502/ {' accept-encoding ': 'gzip, deflate', 'accept-language ':' zh-cn,zh; Q =0.9', 'user-agent ': 'Mozilla/5.0 (Windows NT 10.0; Win64; X64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/86.0.4240.198 Safari/537.36', 'Authorization': 'Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1aWQiOjUwMCwicmlkIjowLCJpYXQiOjE2MTQ0MTMyMTAsImV4cCI6MTYxNDQ5OTYxMH0.cZTYLARKNj8 SKlPGPdIUh9RmyQaYAJnJrLObaKiNiU4 '} request parameters: {' username ':' tery ', 'password: 566,' the timer: 1614413210, 'timer_str' : Upload file: '1614413210'} None response data: {" data ": {" id" : 502, 'username' : 'linken', 'role_id: 34}, "meta" : {' MSG' : 'updated', 'status' : 200}} 16:06:50. 2021-02-27 835 | INFO | view data_process: save_response: 27 - add key: Case_002, corresponding value: {" data ": {" id" : 502, 'the username:' linken ', 'role_id: 34}, "meta" : {' MSG' : 'updated', 'status' : 835 | 200}} 2021-02-27 16:06:50. INFO | view data_process: assert_result: 115 - the first assertion, the actual result: {' MSG ':' updated ', 'status' : 200} | expected results: {' MSG ':' setting state SUCCESS ', 'status' : 200} assertion results False 16:06:53. 2021-02-27 418 | SUCCESS | __main__ : run: 43 - report has been generatedCopy the code

Analysis of the log

@sum_data(&$.case_001.data.id&, 2) @sum_data(&$.case_001.data.id&, 2) @sum_data Then call the function sum_data(500, 2), then run the function and replace the result 502 with @sum_data(&$.case_001.data.id&, 2)@.

3. Custom function implementation code

Because what the use case is reading out is a string, and I thought reflection would do that, and I went to the data, and THEN I found getattr(), which was a built-in function, but it didn’t solve the problem of the use case taking arguments, and then I looked at exec(), This function can execute strings of Python code, however I ran into a problem that the Python code variables executed in this function could not be used in other functions. Finally I found the data and used locals() inside the function to get a dictionary of local variables. Fetch variables from exec by dictionary values

tools/hooks.py

#! /usr/bin/env python # _*_ coding: utf-8 _*_ """ @project: apiAutoTest @file: hooks.py @author: zy7y @time: 2021/2/27 @site: https://cnblogs.com/zy7y @github: https://github.com/zy7y @gitee: https://gitee.com/zy7y @desc: Extension method, 2021/02/27 For information about exec executing Python code: https://python3-cookbook.readthedocs.io/zh_CN/latest/c09/p23_executing_code_with_local_side_effects.html """ import time Exec_func (execc: STR) -> STR: exec_func(execc: STR) -> STR: exec_func(execc: STR) -> STR: """ Return will return a result of type STR. """ # get a local dictionary of variables to correct variables in exec, Loc = locals() exec(f"result = {func}") return STR (loc['result']) def get_current_highest(): """ return int(time.time()) def sum_data(a, b): """ "" return a + bCopy the code

tools/__init__.py

I find it too long to write here, so I will post the local code here

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

from tools.hooks import *


.Above code omitteddef 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)))
        logger.debug(f"&{ctt}The value of the & replacement is{str(extractor(data, ctt))} ")
        
    # add calls to custom functions written in tools/ links.py
    for func in re.findall('@ (. *?) @ ', content):
        content = content.replace(f'@{func}@ ', exec_func(func))
        logger.debug(F "executes the hooks function{func}The value of substitution is zero{exec_func(func)}")

    return content

.The following code is omittedCopy the code

4. Source address

Making: www.github.com/zy7y/apiAut…

Gitee: www.gitee.com/zy7y/apiAut…

The earliest version, which uses dictionary iteration to deal with dependencies, is in version1.0

5. Thanks

Thanks to all the people who gave me help, articles, students who are learning or using apiAutoTest, actually I have gained a sense of achievement since opening this project personally, thank you very much

References:

python3-cookbook

6. Past articles

ApiAutoTest: open source

ApiAutoTest: Use the RE library to handle data dependencies prior to the interface

ApiAutoTest: Increase data isolation (backup/restore database before and after test)

ApiAutoTest: Adds custom functions that can be called in use cases