testtp

Http client for testers


License
Apache-2.0
Install
pip install testtp==0.0.3

Documentation

Testtp

http client for testers

Supported Features


Built with


Installation

python -m pip install testtp

默认参数


Session级默认参数

Session继承自requests.Session.利用requests.Session实现默认数据。

from testtp import Session
s = Session()
s.headers['X-request-type'] = 'autotest'
s.params = {'default_params': 'value'}
s.get('http://httpbin.org/get', params={'custom': 'query'}, headers={'X-token': 'loda'}) 

>>> OUTPUT
2020-12-05 19:26:03.677 | SEND     | testtp.Req:_restore_resp:22 - <= request url => 
http://httpbin.org/get?default_params=value&custom=query
2020-12-05 19:26:03.682 | RECV     | testtp.Req:_restore_resp:23 - <= response data => 
{
    "args": {
        "custom": "query",   # 本次请求中的params
        "default_params": "value"    # Session中所有请求的默认params
    },
    "headers": {
        "Accept": "*/*",
        "Accept-Encoding": "gzip, deflate",
        "Host": "httpbin.org",
        "User-Agent": "python-requests/2.25.0",
        "X-Amzn-Trace-Id": "Root=1-5fcb6e4c-1d48a5716e5a0a1b2328d089",
        "X-Request-Type": "autotest",  # Session中所有请求默认header
        "X-Token": "loda"     # 本次请求中的header
    },
    "origin": "159.138.88.145",
    "url": "http://httpbin.org/get?default_params=value&custom=query"
}

API级别默认参数

使用defaultdata装饰器实现添加接口默认参数

class Client(Session):
    def __init__(self, host):
        self.host = host
        super().__init__()

    # 定义api接口,添加默认header和body数据
    @defaultdata(
        headers={"X-API-header": "default-API"},
        json={"default-body-data": "some_api"}
    )
    def some_api(self, **kwargs):
        self.post(self.host+'/post', **kwargs)


c = Client('http://httpbin.org')
c.some_api(json={'real': 'hadogen'})

>>> OUTPUT
2020-12-06 21:44:47.948 | SEND     | testtp.Req:_wrapper:35 - <= request body => 
{
    "default-body-data": "some_api",  # API默认参数
    "real": "hadogen"                 # 本次请求参数
}
2020-12-06 21:44:48.432 | SEND     | testtp.Req:_restore_resp:22 - <= request url => 
http://httpbin.org/post
2020-12-06 21:44:48.438 | RECV     | testtp.Req:_restore_resp:23 - <= response data => 
{
    "args": {},
    "data": "{\"default-body-data\": \"some_api\", \"real\": \"hadogen\"}",
    "files": {},
    "form": {},
    "headers": {
        "Accept": "*/*",
        "Accept-Encoding": "gzip, deflate",
        "Content-Length": "52",
        "Content-Type": "application/json",
        "Host": "httpbin.org",
        "User-Agent": "python-requests/2.25.0",
        "X-Amzn-Trace-Id": "Root=1-5fcce04f-26e703c65519aff126e74acf",
        "X-Api-Header": "default-API"    #API-默认参数
    },
    "json": {
        "default-body-data": "some_api",  # API默认参数
        "real": "hadogen"                 # 本次请求参数
    },
    "origin": "61.149.134.32",
    "url": "http://httpbin.org/post"
}

数据动态渲染

使用预定义的规则标识数据中的变量,执行时会将testdata对应位置的值做替换

*用字符串规则来标识函数和变量,使得测试数据为json格式,方便数据的存储于外部文件或数据库


变量缓存---cache

“{{ x }}"字符串括起来的数据x被认为是变量,运行时将替换成cache中的对应数据。

s = Session()
s.cache = {'token_holder': 'real_token'}  # 设置缓存数据
body={'data': 'testdata', 'token': '{{ token_holder }}'}
s.post('http://httpbin.org/post', json=body)
>>> OUTPUT
2020-12-06 22:07:40.964 | SEND     | testtp.Req:_wrapper:35 - <= request body => 
{
    "data": "testdata",
    "token": "real_token"  # token被替换成cache中的数据
}

上下文数据缓存---stash

Session将最近一次请求的response存储。使用stash函数可以从response数据抽取关键的数据,存储到cache,供后面请求使用, json_query提取规则为jmespath规则

s = Session()
s.cache = {'token_holder': 'real_token'}
body={'data': 'testdata', 'token': '{{ token_holder }}'}
s.post('http://httpbin.org/post', json=body)
s.stash(json_query='headers.Host', key='host') # 将返回的content中 'header.Host'字段存到缓存cache中,key为'host'
print(self.cache)

>>> OUTPUT
{"token_holder": "real_token", "host": "httpbin.org"}

body={'context_vars': '{{ host }}'}
s.post('http://httpbin.org/post', json=body)

>>> OUTPUT
2020-12-06 22:07:41.644 | SEND xx   | testtp.Req:_wrapper:35 - <= request body => 
{
    "context_vars": "httpbin.org"
}

使用函数实时计算参数---register_render

“{% f(x) %}"字符串括起来的数据f(x)被认为是函数,运行时将执行函数来生成数据。
支持python内置函数例如random.randint(), 也可以用register_render将自定义函数注册给渲染器使用。

def get_timestamp(shift: int):
        return int(time.time()) + shift
s = Session()
s.register_render(get_timestamp) 
body={'data': 'testdata', 'timestamp': '{% get_timestamp(100) %}'}
s.post('http://httpbin.org/post', json=body)

>>> OUTPUT
2020-12-06 22:36:32.784 | SEND     | testtp.Req:_wrapper:35 - <= request body => 
{
    "data": "testdata",
    "timestamp": 1607265492  # 实际参数为get_timestamp函数返回的当前时间戳
}

数据校验


校验返回body---validate

validate方法对response进行格式校验, scm与请求数据一样会经过render处理,支持变量和函数规则同"{{ x }}", "{% f %}".

*注意schema中的函数只传函数名,执行时会将response对应位置的值作为参数调用f,函数f的返回,应是一个bool值。

def is_url(s):
    return s.startswith('http')
s = Session()
s.get('http://httpbin.org/get')
scm = {
    "args": {},
    "headers": {
        "Accept": "{% str %}",
        "Accept-Encoding": "{% str %}",
        "Host": "httpbin.org",
        "User-Agent": "{% str %}",
        "X-Amzn-Trace-Id": "{% str %}"
    },
    "origin": "{% str %}",
    "url": "http://httpbin.org/get"
}
s.validate(scm)

>>> OUTPUT
2020-12-07 14:40:05.689 | SEND     | testtp.Req:_restore_resp:22 - <= request url => 
http://httpbin.org/get
2020-12-07 14:40:05.691 | RECV     | testtp.Req:_restore_resp:23 - <= response data => 
{
    "args": {},
    "headers": {
        "Accept": "*/*",
        "Accept-Encoding": "gzip, deflate",
        "Host": "httpbin.org",
        "User-Agent": "python-requests/2.25.0",
        "X-Amzn-Trace-Id": "Root=1-5fcdce45-6e7808ba224497484deb1bf1"
    },
    "origin": "223.223.188.146",
    "url": "http://httpbin.org/get"
}
2020-12-07 14:40:05.695 | DEBUG    | testtp.Req:validate:81 - <= schema template => 
{'args': {}, 'headers': {'Accept': <class 'str'>, 'Accept-Encoding': <class 'str'>, 'Host': 'httpbin.org', 'User-Agent': <class 'str'>, 'X-Amzn-Trace-Id': <class 'str'>}, 'origin': <class 
'str'>, 'url': <function test_validate_response_custom_function.<locals>.is_url at 0x000001B478983280>}

数据抽取---json_query

有些场景下需要对返回数据进行一些预处理(如:抽取关键字段,求和,求最大值等),之后才进行校验,这时需要传入数据处理规则json_query,提取规则为jmespath规则

data = {
    'data_list': [
        {'type': 'A', 'count': 80},
        {'type': 'B', 'count': 90},
        {'type': 'C', 'count': 70},
        {'type': 'D', 'count': 50},
        ]
    }
url = 'http://httpbin.org/post'
s = Session()
s.post(url, json=data)
scm = ['A', 'B', 'C', 'D']
s.validate(schema=scm, json_query='json.data_list[].type')

>>> OUTPUT
2020-12-07 14:38:31.241 | RECV     | testtp.Req:_restore_resp:23 - <= response data => 
{
    "args": {},
    "data": "{\"data_list\": [{\"type\": \"A\", \"count\": 80}, {\"type\": \"B\", \"count\": 80}, {\"type\": \"C\", \"count\": 80}, {\"type\": \"D\", \"count\": 80}]}",
    "files": {},
    "form": {},
    "headers": {
        "Accept": "*/*",
        "Accept-Encoding": "gzip, deflate",
        "Content-Length": "127",
        "Content-Type": "application/json",
        "Host": "httpbin.org",
        "User-Agent": "python-requests/2.25.0",
        "X-Amzn-Trace-Id": "Root=1-5fcdcde7-332bb4ee193f31915faee06b"
    },
    "json": {
        "data_list": [
            {"count": 80,"type": "A"},
            {"count": 90,"type": "B"},
            {"count": 70,"type": "C"},
            {"count": 50,"type": "D"},
        ]
    },
    "origin": "223.223.188.146",
    "url": "http://httpbin.org/post"
}
2020-12-07 14:38:31.246 | DEBUG    | testtp.Req:validate:73 - <= json query => 
json.data_list[].type
2020-12-07 14:38:31.247 | DEBUG    | testtp.Req:validate:74 - <= extract body => 
['A', 'B', 'C', 'D']
2020-12-07 14:38:31.248 | DEBUG    | testtp.Req:validate:81 - <= schema template => 
['A', 'B', 'C', 'D']