封装了一些,测试人员写自动化代码的常用功能作为装饰器,目标是能让测试人员更关注业务逻辑的实现,隐藏和业务逻辑无关的细节
通过BigTangerine 来定义接口, 然后通过MetaData 来定义需要验证的数据对象,在BigTangerine中定义的接口。
主要的入口类,所有的装饰器都是应用在该类或其子类的方法上 示例代码
class MockService(BigTangerine):
@cjen.http.base_url(uri="http://127.0.0.1:5000")
def __init__(self): super().__init__()
@cjen.headers.contentType(value="application/json")
@cjen.http.post_mapping(uri="post_method_json")
def post_method_json(self, *, data, resp=None, **kwargs):
assert resp.get("procCode") == 200# 继承BigTangerine
class 自定义类名(BigTangerine):
# 定义__init__函数,通常在该函数定义访问的连接和定义基准的Header
@cjen.http.base_url(uri="http://127.0.0.1:5000") # 定义访问连接
@cjen.headers.basicHeaders(headers=dict(Header的名=Header的值)) # 定义基准的Header
def __init__(self): super().__init__()
# 定义方法. 直接返回接口数据
# 不需要传入参数
# 且 不需要生成meta对象
# 且 不需要逻辑处理
@cjen.http.(get|put|delete|uploading)_mapping(uri="api连接名")
def 方法名(self,*, resp=None, **kwargs): ...
# 定义方法. 传入参数为json,仍会返回接口返回数据,自定义逻辑
# 不需要生成meta对象
# data为固定参数名称
@cjen.http.(get|put|delete|uploading)_mapping(uri="api连接名")
def 方法名(self,*,data, resp=None, **kwargs):
## 自定义操作代码 resp 为接口返回的数据 ##
# 定义方法. api带有参数,仍会返回接口返回数据,自定义逻辑
# 不需要生成meta对象
# path_variable为固定参数名称
@cjen.http.(get|put|delete|uploading)_mapping(uri="api连接名")
def 方法名(self,*,path_variable, resp=None, **kwargs):
## 自定义操作代码 resp 为接口返回的数据 ##
# 定义方法. 带有Meta 对象,已MetaJson为例
@cjen.http.(get|put|delete|uploading)_mapping(uri="api连接名")
def 方法名(self,*, meta对象名:Meta类型 resp=None, **kwargs): ...如果接口有依赖关系,则可以使用"上下文"来做值传递
class Customer(BigTangerine):
# 通过 装饰器添加 context
@orange.context.add(content=dict(key=value))
def __init__(self): super().__init__()
# 通过 方法控制
def controal_context(self): self.context.update(dict(kev=value))根据Json对象生成的数据对象,可用json_path来获取期望的值
class C01Employees(MetaJson):
@cjen.operate.common.value
@cjen.operate.json.many(json_path="$.employees[?(@.company_id==1)]", filter_keys=["name", "company_id"])
def employees(self): ...
@cjen.operate.common.value
@cjen.operate.json.one(json_path="$.procCode")
def procCode(self): ...不兼容 Python 自带装饰器 @property
查询mysql数据库生成数据对象
class Company(MetaMysql):
@cjen.operate.common.value
def id(self): ...
@cjen.operate.common.value
def name(self): ...不兼容 Python 自带装饰器 @property
| 装饰器 | 参数 | 作用 | 代码示例 |
|---|---|---|---|
| @cjen.headers.basicHeaders | headers(required): dict | 初始化 Basic Header, Basic Header 是作为基准 Header 存在 | 定义基准Headers |
| @cjen.headers.appendBasicHeaders | headers(required): dict | 增加 Basic Header | 增加基准Headers |
| @cjen.headers.addHeaders | headers(required): dict | 新增临时 Header | 添加临时Headers |
| @cjen.headers.contentType | value(required): str | 新增临时 Content-Type Header | 添加临时Content-Type |
| @cjen.headers.accept | value(required): str | 新增临时 Accept Header | 添加临时Accept |
| 装饰器 | 参数 | 作用 | 代码示例 |
|---|---|---|---|
| @cjen.http.post_mapping | uri(required): str | 发送 Post 请求,并返回结果 | |
| @cjen.http.get_mapping | uri(required): str | 发送 GET 请求,并返回结果 | 带参url |
| @cjen.http.put_mapping | uri(required): str | 发送 PUT 请求,并返回结果 | |
| @cjen.http.delete_mapping | uri(required): str | 发送 delete 请求,并返回结果 | |
| @cjen.http.upload_mapping | uri(required): str | 发送 上传文件 请求,并返回结果 | 上传文件 |
| @cjen.http.base_url | uri(required): str | 设置访问的地址 | 设置访问地址 |
| 装饰器 | 参数 | 作用 | 代码示例 |
|---|---|---|---|
| @cjen.operate.mysql.factory |
|
创建MetaMysql对象 | |
| @cjen.operate.json.factory | clazz(required): MetaJson | 创建 MetaJson对象 | 创建Json对象 |
| 装饰器 | 参数 | 作用 | 代码示例 |
|---|---|---|---|
| @cjen.operate.asserts.validation_meta |
|
执行某一个Meta 对象的assert 装饰器 |
| 装饰器 | 参数 | 作用 | 代码示例 |
|---|---|---|---|
| @cjen.operate.common.value | 无参数 | 返回值 | |
| @cjen.operate.json.listOf | clazz(required) | 当返回值为多个时,生成对象 list,需配合 @cjen.operate.json.many 使用 | 转换为对象list |
| 装饰器 | 参数 | 作用 | 代码示例 |
|---|---|---|---|
| @cjen.operate.json.one | json_path(required): str | 从json对象中某一个值 | 选取指定值 |
| @cjen.operate.json.many |
|
从json对象中获取满足条件的值 | 提取多个值 |
| 装饰器 | 参数 | 作用 | 代码示例 |
|---|---|---|---|
| @cjen.operate.asserts.equal | value(required): str or int | 等于 | 等于 |
| @cjen.operate.asserts.not_equal | value(required): str or int | 不等于 | 不等于 |
| @cjen.operate.asserts.in_range | value(required): list | 在范围中 | 在范围内 |
| @cjen.operate.asserts.not_in_range | value(required): list | 不在范围中 | 不在范围内 |
| @cjen.operate.asserts.required | 无参数 | 必填 | 必填 |
| 装饰器 | 参数 | 作用 | 代码示例 |
|---|---|---|---|
| @cjen.operate.common.value | 无参数 | 如果期望Meta Data 的Field 返回值,则需要顶层使用该装饰器 |
| 装饰器 | 参数 | 作用 | 代码示例 |
|---|---|---|---|
| @cjen.operate.asserts.equal | value(required): str or int | 等于 | 等于 |
| @cjen.operate.asserts.not_equal | value(required): str or int | 不等于 | 不等于 |
| @cjen.operate.asserts.in_range | value(required): list | 在范围中 | 在范围内 |
| @cjen.operate.asserts.not_in_range | value(required): list | 不在范围中 | 不在范围内 |
| @cjen.operate.asserts.required | 无参数 | 必填 | 必填 |
class HeaderMockService(BigTangerine):
# 初始化基准Header
@cjen.headers.basicHeaders(headers=dict(Basicheader="1"))
def __init__(self): super().__init__()class HeaderMockService(BigTangerine):
@cjen.headers.basicHeaders(headers=dict(Basicheader="1"))
def __init__(self): super().__init__()
# 在请求的Header中添加基准Header
@cjen.headers.appendBasicHeaders(headers=dict(Appendbasicheaders="2"))
@cjen.http.get_mapping(uri="test_headers")
def append_basic_headers(self, resp=None, **kwargs):
assert resp.get("headers")["Basicheader"] == self.headers["Basicheader"]
assert resp.get("headers")["Appendbasicheaders"] == self.headers["Appendbasicheaders"]class HeaderMockService(BigTangerine):
@cjen.http.base_url(uri="http://127.0.0.1:5000")
@cjen.headers.basicHeaders(headers=dict(Basicheader="1"))
def __init__(self): super().__init__()
# 在请求的Header中添加临时的Header
@cjen.headers.addHeaders(headers=dict(Newheader="3"))
@cjen.http.get_mapping(uri="test_headers")
def add_headers(self, resp=None, **kwargs):
assert resp.get("headers")["Basicheader"] == self.headers["Basicheader"]
assert resp.get("headers")["Newheader"] == "3"
assert self.headers.get("Newheader") is None## 添加临时Content-Type
class HeaderMockService(BigTangerine):
@cjen.http.base_url(uri="http://127.0.0.1:5000")
@cjen.headers.basicHeaders(headers=dict(Basicheader="1"))
def __init__(self): super().__init__()
# 在请求的Header中添加临时 Content-Type
@cjen.headers.contentType(value="application/json")
@cjen.http.get_mapping(uri="test_headers")
def add_content_type(self, resp=None, **kwargs):
assert resp.get("headers")["Basicheader"] == self.headers["Basicheader"]
assert resp.get("headers")["Content-Type"] == "4"
assert self.headers.get("Content-Type") is Noneclass HeaderMockService(BigTangerine):
@cjen.http.base_url(uri="http://127.0.0.1:5000")
@cjen.headers.basicHeaders(headers=dict(Basicheader="1"))
def __init__(self): super().__init__()
# 在请求的Header中添加临时 Accept
@cjen.headers.accept(value="5")
@cjen.http.get_mapping(uri="test_headers")
def add_accept(self, resp=None, **kwargs):
assert resp.get("headers")["Basicheader"] == self.headers["Basicheader"]
assert resp.get("headers")["Accept"] == "5"
assert self.headers.get("Accept") is Noneclass MockService(BigTangerine):
# 设置访问的 http 地址
@cjen.http.base_url(uri="http://127.0.0.1:5000")
def __init__(self): super().__init__()
# 请求带参url
# 1. 定义api路径
# 方法定义如 get_method_path_variable,
# 参数定义 path_variable:dict,需定义该参数
# resp, **kwargs 为固定写法
# 调用:MockService().get_method_path_variable(path_variable=dict(id=1))
@cjen.http.get_mapping(uri="get_method_path_variable?id={id}")
def get_method_path_variable(self, *, path_variable: dict, resp=None, **kwargs):
assert resp.get("procCode") == 200
assert resp.get("path_variable") == path_variable.get("id")
# 请求带参url
# 1. 定义api路径
# 方法定义如 get_method_variable,
# 参数定义 path_variable:dict,需定义该参数
# resp, **kwargs 为固定写法
# 调用:MockService().get_method_variable(path_variable=dict(id=1))
@cjen.http.get_mapping(uri="get_method_variable/{id}")
def get_method_variable(self, *, path_variable: dict, resp=None, **kwargs):
assert resp.get("procCode") == 200
assert resp.get("path_variable") == path_variable.get("id")class MockService(BigTangerine):
# 设置访问的 http 地址
@cjen.http.base_url(uri="http://127.0.0.1:5000")
def __init__(self): super().__init__()
# 请求体为json
# 1. 设置Content Type 为JSON
# 2. 定义api路径
# 方法定义如 post_metod_json,
# 参数定义 data:dict,需定义该参数
# resp, **kwargs 为固定写法
#调用: MockService().post_method_json(data=dict(username="xx", pwd="yyy"))
@cjen.headers.contentType(value="application/json")
@cjen.http.post_mapping(uri="post_method_json")
def post_method_json(self, *, data, resp=None, **kwargs):
assert resp.get("procCode") == 200
# 请求带参url
# 1. 定义api路径
# 方法定义如 post_method_variable,
# 参数定义 path_variable:dict,需定义该参数
# resp, **kwargs 为固定写法
# 调用:MockService().post_method_variable(path_variable=dict(id=1))
@cjen.http.post_mapping(uri="post_method_variable/{id}")
def post_method_variable(self, *, path_variable: dict, resp=None, **kwargs):
assert resp.get("procCode") == 200
assert resp.get("path_variable") == path_variable.get("id")
# 请求带参url
# 1. 定义api路径
# 方法定义如 post_method_path_variable,
# 参数定义 path_variable:dict,需定义该参数
# resp, **kwargs 为固定写法
# 调用:MockService().post_method_path_variable(path_variable=dict(id=1))
@cjen.http.post_mapping(uri="post_method_path_variable?id={id}")
def post_method_path_variable(self, *, path_variable: dict, resp=None, **kwargs):
assert resp.get("procCode") == 200
assert resp.get("path_variable") == path_variable.get("id")
# 下载
# 1. 定义api路径
# 方法定义如 download_file,
# 参数定义 resp, **kwargs 为固定写法
# 在方法体中定义文件存储
# 调用:MockService().download_file()
@cjen.http.post_mapping(uri="download_file")
def download_file(self, *, resp=None, **kwargs):
path = os.path.join(os.path.dirname(__file__), "test_download_file")
with open(path, "wb") as f:
f.write(resp)class MockService(BigTangerine):
@cjen.http.base_url(uri="http://127.0.0.1:5000")
def __init__(self): super().__init__()
# 请求体为json
# 1. 设置Content Type 为JSON
# 2. 定义api路径
# 方法定义如 put_method_json,
# 参数定义 data:dict,需定义该参数
# resp, **kwargs 为固定写法
#调用: MockService().put_method_json(data=dict(username="xx", pwd="yyy"))
@cjen.headers.contentType(value="application/json")
@cjen.http.put_mapping(uri="put_method_json")
def put_method_json(self, *, data, resp=None, **kwargs): ...
# 请求带参url
# 1. 定义api路径
# 方法定义如 put_method_variable,
# 参数定义 path_variable:dict,需定义该参数
# resp, **kwargs 为固定写法
# 调用:MockService().put_method_variable(path_variable=dict(id=1))
@cjen.http.put_mapping(uri="put_method_variable/{id}")
def put_method_variable(self, *, path_variable, resp=None, **kwargs): ...
# 请求带参url
# 1. 定义api路径
# 方法定义如 put_method_path_variable,
# 参数定义 path_variable:dict,需定义该参数
# resp, **kwargs 为固定写法
# 调用:MockService().put_method_path_variable(path_variable=dict(id=1))
@cjen.http.put_mapping(uri="put_method_path_variable?id={id}")
def put_method_path_variable(self, *, path_variable, resp=None, **kwargs): ...class MockService(BigTangerine):
@cjen.http.base_url(uri="http://127.0.0.1:5000")
def __init__(self): super().__init__()
# 请求体为json
# 1. 设置Content Type 为JSON
# 2. 定义api路径
# 方法定义如 delete_method_json,
# 参数定义 data:dict,需定义该参数
# resp, **kwargs 为固定写法
#调用: MockService().delete_method_json(data=dict(username="xx", pwd="yyy"))
@cjen.headers.contentType(value="application/json")
@cjen.http.delete_method_json(uri="delete_method_json")
def delete_method_json(self, *, data, resp=None, **kwargs): ...
# 请求带参url
# 1. 定义api路径
# 方法定义如 delete_method_variable,
# 参数定义 path_variable:dict,需定义该参数
# resp, **kwargs 为固定写法
# 调用:MockService().delete_method_variable(path_variable=dict(id=1))
@cjen.http.delete_mapping(uri="delete_method_variable/{id}")
def delete_method_variable(self, *, path_variable: dict, resp=None, **kwargs): ...
# 请求带参url
# 1. 定义api路径
# 方法定义如 delete_method_path_variable,
# 参数定义 path_variable:dict,需定义该参数
# resp, **kwargs 为固定写法
# 调用:MockService().delete_method_path_variable(path_variable=dict(id=1))
@cjen.http.delete_mapping(uri="delete_method_path_variable?id={id}")
def delete_method_path_variable(self, *, path_variable: dict, resp=None, **kwargs): ...class MockService(BigTangerine):
@cjen.http.base_url(uri="http://127.0.0.1:5000")
def __init__(self): super().__init__()
# 1. 定义api路径
# 方法定义如 upload_file,
# 参数定义 data:dict,需定义该参数,该参数中应该包含文件路径, file 是必须的
# resp, **kwargs 为固定写法
#调用: MockService().delete_method_json(data=dict(username="xx", pwd="yyy"))
@cjen.http.upload_mapping(uri="upload_file")
def upload_file(self, *, data, resp=None, **kwargs): ...
if __name__== '__main__':
mock = MockService()
# 关键字 file 是必须的
mock.upload_file(data=dict(file=os.path.join(os.path.dirname(__file__), "upload_source.txt")))
import os
import cjen
from cjen import DatabasePool, BigTangerine, MetaMysql
from cjen.bigtangerine import ContextArgs
from cjen.nene.helper import FileHelper
# 创建 数据库连接
def cursor():
return DatabasePool.cursor(host="127.0.0.1", port=3306, user='root', pwd='123456', database='cjen_test_sql')
# 创建 Company 对象
class Company(MetaMysql):
@cjen.operate.common.value
def id(self): ...
@cjen.operate.common.value
def name(self): ...
# 创建 Employee 对象
class Employee(MetaMysql):
@cjen.operate.common.value
def id(self): ...
@cjen.operate.common.value
def name(self): ...
@cjen.operate.common.value
def company(self): ...
class CMysql(BigTangerine):
def __init__(self): super().__init__()
# 返回一条记录,用一条记录生成 Mysql Meta 对象
@cjen.operate.mysql.factory(cursor=cursor(), clazz=Company, sql="SELECT * FROM company WHERE id = 1")
def get_one_company(self, company: Company, **kwargs): ...
# 返回多条记录,生成一组 list[Mysql Meta] 对象
# size = -1 表示 返回的所有值
# clazz 表示 生成的对象
# 方法定义中:companies: list[Company] 后面的类型指定是必须的
@cjen.operate.mysql.factory(cursor=cursor(), size=-1, clazz=Company, sql="SELECT * FROM company")
def get_many_companies(self, companies: list[Company], **kwargs): ...
# 带参数SQL查询,手动固定参数
@cjen.operate.mysql.factory(cursor=cursor(), clazz=Employee, sql=FileHelper.read(os.path.dirname(__file__), "employees.sql"),
params=[2, 3], size=-1)
def get_e0203_employee(self, employees: list[Employee], **kwargs): ...
# 带参数SQL查询,参数来自上下文
# params: ContexArgs(key: sql中的查询参数 = value: 上下文中的取值参数名)
@cjen.operate.mysql.factory(cursor=cursor(), clazz=Employee,
sql=FileHelper.cur_read(cur=__file__, file="employees_of_company.sql"),
params=ContextArgs(id="company_id"), size=-1)
def get_c01_employees(self, employees: list[Employee], **kwargs): ...
# 设置上下文中的参数
@cjen.context.add(content=dict(company_id=1))
def set_company_id(self): ...
# 定义 MetaJson 对象
class Company(MetaJson):
@cjen.operate.common.value
@cjen.operate.json.one(json_path="$.procCode")
def procCode(self): ...
@cjen.operate.common.value
@cjen.operate.json.one(json_path="$.id")
def id(self): ...
@cjen.operate.common.value
@cjen.operate.json.one(json_path="$.name")
def name(self): ...
class JsonMockService(BigTangerine):
@cjen.http.base_url(uri="http://127.0.0.1:5000")
def __init__(self):
super().__init__()
@cjen.http.get_mapping(uri="company/{company_id}")
# 创建 MetaJson 对象
@cjen.operate.json.factory(clazz=Company)
def get_company(self, *, path_variable: dict, resp=None, company: Company = None, **kwargs): ...class C01Employees(MetaJson):
# 获取匹配的多个值
# filter_keys: 获取指定的Key-Value,如果不设置的则获取所有的Key-Value
@cjen.operate.json.many(json_path="$.employees[?(@.company_id==1)]", filter_keys=["name", "company_id"])
def employees(self): ...
# 选择json对象中的指定值
@cjen.operate.json.one(json_path="$.procCode")
def procCode(self): ...
# 把 @cjen.operate.json.many 中提取的值,转化为 Company list 对象
@cjen.operate.json.listOf(clazz=Company)
@cjen.operate.json.many(json_path="$.employees[?(@.company_id==1)]", filter_keys=["name", "company_id"])
def companies(self): ...class CompanyC01(MetaMysql):
@cjen.operate.common.value
@cjen.operate.asserts.not_equal(value=1)
@cjen.operate.asserts.required # 必填
def id(self): ...
@cjen.operate.common.value
@cjen.operate.asserts.equal(value="C01") # 相等
@cjen.operate.asserts.in_range(ranges=["C01", "C02", "C03", "C04"]) # 在范围值内
def name(self): ...
class Employee(MetaMysql):
@cjen.operate.common.value
def id(self): ...
@cjen.operate.common.value
@cjen.operate.asserts.not_in_range(ranges=["C01", "C02", "C03", "C04"]) # 不在范围值内
def name(self): ...
class AssertMockService(BigTangerine):
@cjen.http.base_url(uri="http://127.0.0.1:5000")
def __init__(self): super().__init__()
@cjen.http.get_mapping(uri="none_field_response")
@cjen.operate.mysql.factory(cursor=cursor(), clazz=CompanyC01,
sql="SELECT * FROM company WHERE id = 1",
params=dict(id=1))
@cjen.operate.json.factory(clazz=ResponseNone)
@cjen.operate.asserts.validation_meta(meta_name="company", fields="id;name") # 检查指定的fields,
@cjen.operate.asserts.validation_meta(meta_name="resp_null") # 检查所有的fields
def validation_appoint(self, resp=None, company: CompanyC01 = None, resp_null: ResponseNone = None, **kwargs): ...通过数据库查询时,在数据库查询之前,调用了requests方法,则数据库在读取数据时
self._scok.settimeout(timeout)
# 该方法调用时间会增加,在查询大数据的时候,会很慢
# 原因暂时没找到,临时解决是调整调用顺序
初始化框架
- get_mapping, put_mapping, delete_mapping, upload_mapping, post_mapping 新增json_clazz参数,非必填,作用相当于operate.json.factory


