Python接口自动化装饰器的大用处
事情是这样,我们正在编写接口自动化用例。因为基本上都是复杂的场景测试。
例如测试支付业务的过程: 用户登录 加入购物 下单 支付
也就是说,如果你想测试支付业务,大概必须要调用前面三个接口。那么我们就需要把前面三个接口进行封装。以用户登录为例。 import json import requests class UserLogin: def __init__(self, username, password): self.username = username self.password = password def get_token(self): """获取用户登录token""" url = "http://httpbin.org/post" data = { "username": self.username, "password": self.password, "token": "token123" # 假装这是接口返回的toKen } r = requests.post(url, data=data) if r.status_code != 200: raise ValueError("接口请求失败") try: r.json() except json.decoder.JSONDecodeError: raise ValueError("接口不是json格式") if r.json()["headers"]["Host"] != "httpbin.org": raise ValueError("接口返回必要参数错误") token = r.json()["form"]["token"] return token if __name__ == "__main__": user_login = UserLogin("zhangsan", "mima123") token = user_login.get_token() print(token)
单看接口这么封装,貌似没有问题~!但每个接口调用之后都需要经历以下过程: 判断状态码是否为 200 ,如果不是 200 说明接口不通。仅接着判断返回值格式是否为 JSON ,如果不是,你就无法提取数据。检查接口返回的必要参数,例如: r.json()["headers"]["Host"] 。提取接口返回的数据。例如: r.json()["form"]["token"] 。python装饰器
装饰器(Decorators)是 Python 的一个重要部分。简单地说:他们是修改其他函数的功能的函数。他有助于让我们的代码更简短,也更Pythonic。
这里就不领着大家一步步推演如何创建一个装饰器,直接看例子。 装饰器 def dec(): """ python装饰器 """ def decorator(func): def wrapper(*args, **kwargs): func_name = func.__name__ print(f"被装饰的方法名: {func_name}") print(f"方法的入参 args: {args}") print(f"方法的入参 kwargs: {kwargs}") r = func(*args, **kwargs) print(f"方法的返回值 return: {r}") return wrapper return decorator
装饰器的架子大概长这个样子,重点在装饰器的入参和返回值。 用法 @dec() def add(a, b): c = a + b return c add(1, 2)
调用 @dec() 装饰器来装饰一个add() 函数
运行结果 被装饰的方法名: add 方法的入参 args: (1, 2) 方法的入参 kwargs: {} 方法的返回值 return: 3
这个装饰器可以拿到被装饰函数的 名字 、入参 、返回值 ,是不是很有意思。接口检查装饰器check_response() 装饰器实现 import json from jmespath import search def check_response( describe: str = "", status_code: int = 200, ret: str = None, check: dict = None, debug: bool = False): """ checkout response data :param describe: interface describe :param status_code: http status code :param ret: return data :param check: check data :param debug: debug Ture/False :return: """ def decorator(func): def wrapper(*args, **kwargs): func_name = func.__name__ if debug is True: print(f"Execute {func_name} - args: {args}") print(f"Execute {func_name} - kwargs: {kwargs}") r = func(*args, **kwargs) flat = True if r.status_code != status_code: print(f"Execute {func_name} - {describe} failed: {r.status_code}") flat = False try: r.json() except json.decoder.JSONDecodeError: print(f"Execute {func_name} - {describe} failed:Not in JSON format") flat = False if debug is True: print(f"Execute {func_name} - response: {r.json()}") if flat is True: print(f"Execute {func_name} - {describe} success!") if check is not None: for expr, value in check.items(): data = search(expr, r.json()) if data != value: print(f"Execute {func_name} - check data failed:{value}") raise ValueError(f"{data} != {value}") if ret is not None: data = search(ret, r.json()) if data is None: print(f"Execute {func_name} - return {ret} is None") return data else: return r.json() return wrapper return decorator 核心就是在前面 @dec() 装饰器的架子上扩展,增加参数和返回值校验。代码引用了 jmespath 库,主要是为了提取数据。使用 import requests class UserLogin: def __init__(self, username, password): self.username = username self.password = password @check_response("获取用户登录token", 200, ret="form.token", check={"headers.Host": "httpbin.org"}, debug=True) def get_token(self): """获取用户登录token""" url = "http://httpbin.org/post" data = { "username": self.username, "password": self.password, "token": "token123" # 假装是接口返回的toKen } r = requests.post(url, data=data) return r if __name__ == "__main__": user_login = UserLogin("zhangsan", "mima123") token = user_login.get_token() print(token)
通过 @check_response() 装饰被调用接口,可以极大的简化代码。参数说明:获取用户登录token : 接口描述。200 : 检查接口返回值状态码是否为 200 。ret="form.token" : 提取接口返回值中的token ,通过jmespath 。check={"headers.Host": "httpbin.org"} : 检查接口返回值中包含的参数。相当于对接口数据进行断言。debug=True : 开启debug,打印详细信息,方便调试。
运行信息 Execute get_token - args: (<__main__.UserLogin object at 0x000001EF4397E1C0>,) Execute get_token - kwargs: {} Execute get_token - response: {"args": {}, "data": "", "files": {}, "form": {"password": "mima123", "token": "token123", "username": "zhangsan"}, "headers": {"Accept": "*/*", "Accept-Encoding": "gzip, deflate", "Content-Length": "49", "Content-Type": "application/x-www-form-urlencoded", "Host": "httpbin.org", "User-Agent": "python-requests/2.25.0", "X-Amzn-Trace-Id": "Root=1-62682337-2cd21bd0599368e54d2063bd"}, "json": None, "origin": "173.248.248.88", "url": "http://httpbin.org/post"} Execute get_token - 获取用户登录token success! token123
有了这个小小的装饰器,我们减少了很多相同的样例代码。