5

小小装饰器大大用处 - 虫师

 2 years ago
source link: https://www.cnblogs.com/fnng/p/16260417.html
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.

小小装饰器大大用处

2022-05-11 23:11  虫师  阅读(13)  评论(0)  编辑  收藏  举报

事情是这样,我们正在编写接口自动化用例。因为基本上都是复杂的场景测试。

例如测试支付业务的过程:

也就是说,如你想测试支付业务,大概必须要调用前面三个接口。那我们就需要把前面三个接口进行封装。以用户登录为例。

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)

单看接口这么封装,貌似没有问题~!但每个接口调用之后都需要经历以下过程:

  1. 判断状态码是否为 200,如果不是 200 说明接口不通。
  2. 仅接着判断返回值格式是否为 JSON,如果不是,你就无法提取数据。
  3. 检查接口返回的必要参数,例如:r.json()["headers"]["Host"]
  4. 提取接口返回的数据。例如: 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:\n {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
  1. 核心就是在前面@dec() 装饰器的架子上扩展,增加参数和返回值校验。
  2. 代码引用了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

有了这个小小的装饰器,我们减少了很多相同的样例代码。最后,python装饰器 YYDS~!


About Joyk


Aggregate valuable and interesting links.
Joyk means Joy of geeK