接口自动化之响应数据封装

在使用requests实现接口自动化时,接口响应的数据没有代码提示,要获取某个接口字段就需要通过[xxx]的方式来获取,实际开发过程中希望能通过.的方式来获取到某个接口响应属性。

方式一、使用dataclass

dataclass是python3.7新推出的特性之一

from dataclasses import dataclass 
@dataclass
class OrderVo:
    id: int 
    orderNo: str 
    ...
    status: str = 'CLOSED'  # 此处可以设置默认值,如果没有传值,则使用这个默认值

使用方式:

OrderVo(**response.json())

但是在实际使用过程中2个问题

  1. 接口增加字段后,初始化会报错,提示参数多了
  2. 后端介绍字段后,初始化又会报错,提示参数少了(此处虽然可以通过添加默认值来规避,但是频繁的修改的Vo的定义也会很麻烦)
  3. 如果对象是嵌套表示的,则每次都需要在__post_init__中定义嵌套对象要如何初始化

方式二、自定义响应对象

首先是元类的定义

import re
from types import GenericAlias


class ModelMetaclass(type):

    def __new__(cls, name, bases, attrs):
        if name == 'Model':
            return type.__new__(cls, name, bases, attrs)
        attrs['__default__'] = {}  # 此处用来保存数据的默认值
        __remove__ = []
        for key, value in attrs.items():
            if key.startswith('__'):  # 跳过双下划綫开头的属性
                continue
            attrs['__default__'][key] = value
            __remove__.append(key)
        for k in __remove__:
            attrs.pop(k)
        return type.__new__(cls, name, bases, attrs)

然后是定义基类

class Model(dict, metaclass=ModelMetaclass):

    def __init__(self, **kwargs):
        super().__init__(**kwargs)
        for key, value in self.__annotations__.items():
            if key in self:
                if isinstance(value, ModelMetaclass):
                    self[key] = value(**self[key])
                elif isinstance(value, GenericAlias):
                    if self[key]:
                        self[key] = [value.__args__[0](**v) for v in self[key]]
                continue
            # 通过__default__拿到默认值,在初始化的时候将默认值赋值给属性,如果没有默认值且初始化的时候不传值就报错,则.get改为[]的方式来获取值即可
            self[key] = self.__default__.get(key, None)

    def __getattr__(self, item):
        try:
            return self[item]
        except KeyError:
            raise AttributeError(f"{self.__class__.__name__!r} object has no attribute {item!r}")

    def __getattribute__(self, item):
        return super().__getattribute__(item)

    def __setattr__(self, item, value):
        self[item] = value

    __show__ = None   # 定义数据的展示字段,如果为None,则展示全部的数据

    def __str__(self):
        show = self.__show__
        if not show:
            show = self.__annotations__.keys()
        if isinstance(show, str):
            show = re.split(r'[,\s]+', show.strip())  # 展示字段支持通过,和空格进行分割
        val = ', '.join(f"{key}={self.__getattr__(key)!r}" for key in show)
        return f'{self.__class__.__name__}({val})'

    __repr__ = __str__

最后是使用效果

# 定义两个对象
class Book(Model):
	name: str
	price: float = 3.4
	author: str = 'none'

	__show__ = 'name'

class Student(Model):
	name: str
	age: int = 3
	books: list[Book]

	__show__ = 'name'
	
print(Student())
# Student(name=None)
print(Student(name='zs'))
# Student(name='zs')
print(Student().books)
# None 
print(Student(**{'name': 'tom', 'age': 10, 'books': [{'name': 'sg', 'price': 1.2}]}).books)
# [Book(name='sg')]
posted @ 2023-12-20 14:33  流浪卷轴  阅读(6)  评论(0编辑  收藏  举报