昨天体验的时候我们分别执行了
httprunner -h
,httprunner startproject demo
,httprunner run demo
,但是源码中其调用了其他文件中的方法所以暂时先不分析cli.py
了,先从根本开始models.py
typing[类型提示]: https://docs.python.org/zh-cn/3/library/typing.html
pydantic[类型校验]: https://pydantic-docs.helpmanual.io/
用上这两个库就有点强类型语言的味儿了
泛型: https://docs.python.org/zh-cn/3/library/typing.html#generics
枚举: https://docs.python.org/zh-cn/3/library/enum.html
import os # 系统包 from enum import Enum # 枚举类 from typing import Any # Any 表示 任意类型 from typing import Dict # dict 的泛型版本。 from typing import Text # Text 是 str 的别名 from typing import Union # 联合类型;Union[X, Y] 的意思是,非 X 即 Y from typing import Callable # 可调类型; Callable[[int], str] 是把(int)转为 str 的函数。 from typing import List # list 的泛型版本。 from pydantic import BaseModel # pydantic定义对象的基类 from pydantic import Field # pydantic 中 字段扩展定义 from pydantic import HttpUrl # 校验url地址的
该系列中个人对类型的看法/叫法如下
- Text => str / 文本
- List => list / 列表
- Dict => dict / 字典
Name = Text # Name 的本质 其实就是 Text(Text 本质又是 str) Url = Text BaseUrl = Union[HttpUrl, Text] # 是url 或者 Text 两者之一 VariablesMapping = Dict[Text, Any] # key 是 Text ,value 是任意类型 FunctionsMapping = Dict[Text, Callable] # key 是 Text, value是可调用对象 Headers = Dict[Text, Text] # key 是 str, value 也是 str Cookies = Dict[Text, Text] # 同上 Verify = bool # 布尔类型 Hooks = List[Union[Text, Dict[Text, Text]]] # 列表,列表中的元素是 str 或者 key,value 都是 str Export = List[Text] # 列表,列表中元素是 str Validators = List[Dict] # 列表,列表中元素是 字典 Env = Dict[Text, Any] # 字典 key 是 str, value 是任意类型
# 枚举类,其中属性是 Text类型 class MethodEnum(Text, Enum): GET = "GET" POST = "POST" PUT = "PUT" DELETE = "DELETE" HEAD = "HEAD" OPTIONS = "OPTIONS" PATCH = "PATCH"
避免篇幅过长,这里直接复制源代码 附上注释
class TConfig(BaseModel): """测试配置模型""" name: Name verify: Verify = False base_url: BaseUrl = "" # Text: prepare variables in debugtalk.py, ${gen_variables()} # 变量 variables: Union[VariablesMapping, Text] = {} parameters: Union[VariablesMapping, Text] = {} # setup_hooks: Hooks = [] # teardown_hooks: Hooks = [] export: Export = [] path: Text = None weight: int = 1 class TRequest(BaseModel): """测试请求模型""" """requests.Request model""" method: MethodEnum # 这里的类型是前面定义的请求方法枚举 url: Url # 查询参数 params: Dict[Text, Text] = {} headers: Headers = {} # alias 是别名, json 数据 req_json: Union[Dict, List, Text] = Field(None, alias="json") # data 数据 - 表单 data: Union[Text, Dict[Text, Any]] = None cookies: Cookies = {} timeout: float = 120 # 允许重定向 allow_redirects: bool = True # 安全验证 verify: Verify = False upload: Dict = {} # used for upload files class TStep(BaseModel): """测试步骤模型""" name: Name # 步骤可以是一个请求模型 request: Union[TRequest, None] = None # 用例 testcase: Union[Text, Callable, None] = None # 变量 variables: VariablesMapping = {} setup_hooks: Hooks = [] teardown_hooks: Hooks = [] # used to extract request's response field # 提取响应字段 extract: VariablesMapping = {} # used to export session variables from referenced testcase # 导出字段 export: Export = [] # 验证器 validators: Validators = Field([], alias="validate") # 验证脚本 validate_script: List[Text] = [] class TestCase(BaseModel): """测试用例模型 = 测试配置 + 测试步骤""" config: TConfig teststeps: List[TStep] class ProjectMeta(BaseModel): """项目配置模型""" # debugtalk.py 文件内容 debugtalk_py: Text = "" # debugtalk.py file content debugtalk_path: Text = "" # debugtalk.py file path # .env 文件路径 dot_env_path: Text = "" # .env file path # 在 debugtalk.py 中定义的函数 functions: FunctionsMapping = {} # functions defined in debugtalk.py env: Env = {} # 项目根目录 RootDir: Text = os.getcwd() # project root directory (ensure absolute), the path debugtalk.py located class TestsMapping(BaseModel): """测试集合""" project_meta: ProjectMeta testcases: List[TestCase] class TestCaseTime(BaseModel): """测试用例时间""" start_at: float = 0 start_at_iso_format: Text = "" duration: float = 0 class TestCaseInOut(BaseModel): """测试用例输入输出""" # 输入参数 config_vars: VariablesMapping = {} # 导出参数 export_vars: Dict = {} class RequestStat(BaseModel): """请求状态""" content_size: float = 0 response_time_ms: float = 0 elapsed_ms: float = 0 class AddressData(BaseModel): """地址数据""" client_ip: Text = "N/A" client_port: int = 0 server_ip: Text = "N/A" server_port: int = 0 class RequestData(BaseModel): """请求数据模型""" method: MethodEnum = MethodEnum.GET url: Url headers: Headers = {} cookies: Cookies = {} body: Union[Text, bytes, List, Dict, None] = {} class ResponseData(BaseModel): """响应数据模型""" status_code: int headers: Dict cookies: Cookies encoding: Union[Text, None] = None content_type: Text body: Union[Text, bytes, List, Dict] class ReqRespData(BaseModel): """请求响应数据模型""" request: RequestData response: ResponseData class SessionData(BaseModel): """会话数据""" """request session data, including request, response, validators and stat data""" success: bool = False # in most cases, req_resps only contains one request & response # while when 30X redirect occurs, req_resps will contain multiple request & response req_resps: List[ReqRespData] = [] stat: RequestStat = RequestStat() address: AddressData = AddressData() validators: Dict = {} class StepData(BaseModel): """步骤数据模型""" """teststep data, each step maybe corresponding to one request or one testcase""" success: bool = False name: Text = "" # teststep name data: Union[SessionData, List['StepData']] = None export_vars: VariablesMapping = {} StepData.update_forward_refs() class TestCaseSummary(BaseModel): """测试用例结果""" name: Text success: bool case_id: Text time: TestCaseTime in_out: TestCaseInOut = {} log: Text = "" step_datas: List[StepData] = [] class PlatformInfo(BaseModel): httprunner_version: Text python_version: Text platform: Text class TestCaseRef(BaseModel): name: Text base_url: Text = "" testcase: Text variables: VariablesMapping = {} class TestSuite(BaseModel): """测试套件""" config: TConfig testcases: List[TestCaseRef] class Stat(BaseModel): """结果集状态""" total: int = 0 success: int = 0 fail: int = 0 class TestSuiteSummary(BaseModel): """测试套件结果收集""" success: bool = False stat: Stat = Stat() time: TestCaseTime = TestCaseTime() platform: PlatformInfo testcases: List[TestCaseSummary]
上述内容个人理解,如有错误欢迎指出交流。