大家好~我是
米洛
!
我正在从0到1打造一个开源的接口测试平台, 也在编写一套与之对应的教程
,希望大家多多支持。
欢迎关注我的公众号米洛的测开日记
,获取最新文章教程!
# 回顾
上一节我们编写了提取参数
部分,我们已经可以拿到组成一个用例的全部数据了。
接着我们就来实现它。老规矩,先捋一捋思路。
# 思路
我们在页面上可以录制到具体的接口参数,其实每一条记录都是RequestInfo的具体表现。所以,我们根据用户选择的RequestInfo数组,按照他们的顺序,就可以提取为具体的用例数据,接着我们调用之前编写过的提交用例全部数据
方法就完事了。
# 调整枚举类
在此之前,我做了一个改动
:将曾经config.py里面的枚举类或者子类都抽离出来,放到enums
文件夹。
这里就不得不思考一下这样做的好处,最近几天也是感悟了一些,以下是我的个人看法。
有个朋友一直很纠结于他的项目目录设计,我仔细考虑了一下,得出了以下的看法(个人观点):
每个人都会有自己的命名习惯或者目录规范,比如有的人喜欢用util有的喜欢用utils,或者说有的人喜欢dao dto entity等Java风格的命名,有的喜欢用src,internal这样的内部包命名。但这些,其实都不是最重要的。
所谓规范
,其实就是为了给其他人理解,并更好地对代码文件分类,比如我这里有个enums文件夹,那么不用想,进去就知道他是放枚举类的,如果我放一个orm在里面,那自然是不合适的。所以基本的目录含义满足了,也就没必要过于纠结目录规不规范了。如果作为一个团队,团队肯定有对应的规范,如果是个人,那你自己就是规范。
- 添加2个枚举类
app/enums/CaseStatusEnum.py
# 1: 调试中 2: 暂时关闭 3: 正常运作
from enum import IntEnum
class CaseStatus(IntEnum):
debugging = 1
closed = 2
running = 3
2
3
4
5
6
7
8
app/enums/RequestTypeEnum.py
from enum import IntEnum
class RequestType(IntEnum):
http = 1
grpc = 2
dubbo = 3
websocket = 4
2
3
4
5
6
7
8
9
# 把RequestInfo放入schema
由于RequestInfo在用例生成页面即将成为一个入参
,所以他是适合放到schema的:
__author__ = "woody"
import json
from typing import TypeVar
import pydantic
from loguru import logger
"""
translate mitmproxy request and response data
"""
body = TypeVar("body", bytes, str)
class RequestInfo(pydantic.BaseModel):
url: str
body: str
request_method: str
# request_data: str
request_headers: dict
response_headers: dict
cookies: dict
request_cookies: dict
response_content: str
status_code: int
def __init__(self, flow=None, **kwargs):
if flow:
kwargs.update(
dict(status_code=flow.response.status_code,
url=flow.request.url,
request_method=flow.request.method,
request_headers=dict(flow.request.headers),
response_headers=dict(flow.response.headers),
response_content=self.get_response(flow.response),
body=self.get_body(flow.request),
cookies=dict(flow.response.cookies),
request_cookies=dict(flow.request.cookies),
))
super().__init__(**kwargs)
def from_dict(self, **kwargs):
for k, v in kwargs:
if not hasattr(self, k):
raise Exception(f"set RequestInfo error, no field: {k}")
setattr(self, k, v)
@classmethod
def translate_json(cls, text):
try:
return json.dumps(json.loads(text), indent=4, ensure_ascii=False)
except Exception as e:
logger.bind(name=None).warning(f"解析json格式请求失败: {e}")
return text
@classmethod
def get_response(cls, response):
content_type = response.headers.get("Content-Type")
if "json" in content_type.lower():
return cls.translate_json(response.text)
if "text" in content_type.lower() or "xml" in content_type.lower():
return response.text
return response.data.decode('utf-8')
@classmethod
def get_body(cls, request):
if len(request.content) == 0:
return ''
content_type = request.headers.get("Content-Type")
if "json" in content_type.lower():
return cls.translate_json(request.text)
if "text" in content_type.lower() or "xml" in content_type.lower():
return request.text
return request.data.decode('utf-8')
def dumps(self):
return json.dumps(self.dict(), ensure_ascii=False)
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
基本是从app/core/request/request.py里面拷贝来的代码,不过这里多了一种情况:
由于pydantic的初始化不适配mitmproxy的初始化,也就是flow与普通的__init__(),所以我这把进行了一下判断。
如果有flow,说明是录制
的时候),否则说明是请求参数初始化的时候。
# testcase_schema.py新增一个类
class TestCaseGeneratorForm(BaseModel):
directory_id: int
requests: List[RequestInfo]
2
3
4
由于一个用例必须有自己归属的目录
,所以我们需要为它归属一个目录,其实还需要用例名称等等,但我们目前只做一个雏形
,后面可以慢慢完善,比如我们给一个默认值就好了。
新的ide配色还有点不习惯,今天的内容就到这里了,下一节我们继续深入。