一、接口设计
接口名称:/test_steps/run/
请求方式: POST
参数格式: JSON
请求参数:

请求示例: json格式参数
{
"data": {
"id": 2,
"interface": {
"url": "/users/login/",
"method": "POST"
},
"title": "登录失败",
"headers": {},
"request": {
"json": {
"username": "xinlan",
"password": "123123"
},
"params": {}
},
"file": [],
"setup_script": "# 前置脚本(python):\n# global_tools:全局工具函数\n# data:用例数
据 \n# env: 局部环境\n# ENV: 全局环境\n# db: 数据库操作对象\n",
"teardown_script": "# 后置脚本(python):\n# global_tools:全局工具函数\n# data:用
例数据 \n# response:响应对象response \n# env: 局部环境\n# ENV: 全局环境\n# db: 数据库操
作对象\n"
},
"env": 1
}
返回示例:
- 响应状态码:200
- 响应数据:
{
"name": "登录失败",
"log_data": [
["INFO", "【INFO】 | 开始执行用例:【登录失败】\n"],
["DEBUG", "临时变量:\n{}"],
["DEBUG", "全局变量:\n{'host': 'http://127.0.0.1:8080',
'headers': {
'customer-header': 'wahaha'
}, 'key1': 'value1', 'key2': 'value2'
}
"], ["INFO", "【INFO】 | *********执行前置脚本*********"],
["INFO", "【INFO】 | 发送
[POST] 请求: 请求地址为http: //127.0.0.1:8080/users/login/:"],["DEBUG","请求头:
\n {
'User-Agent': 'python-requests/2.28.1',
'Accept-Encoding': 'gzip, deflate',
'Accept': '*/*',
'Connection': 'keep-alive',
'customer-header': 'wahaha',
'Content-Length': '44',
'Content-Type': 'application/json'
}
"],["
DEBUG ","
请求体:
\ n {\
n\ "username\": \"xinlan\",\n \"password\": \"123123\"\n}"],
["INFO", "【INFO】 | 请求响应状态码:401"],
["DEBUG", "响应头:\n{'Date': 'Thu, 21 Jul
2022 12: 42: 41 GMT ', '
Server ': '
WSGIServer / 0.2 CPython / 3.8 .5 ', '
Content - Type ':
'application/json', 'WWW-Authenticate': 'Bearer realm=\"api\"', 'Vary': 'Accept,
Origin ', '
Allow ': '
POST, OPTIONS ', '
X - Frame - Options ': '
DENY ', '
Content - Length ':
'37', 'X-Content-Type-Options': 'nosniff', 'Referrer-Policy': 'same-origin'
}
"], ["DEBUG", "响应体:\n{\n \"detail\": \"用户名密码错误!\"\n}"],
["INFO", "【INFO】 | **
** ** ** * 执行后置脚本 ** ** ** ** * "],["
INFO ","【
INFO】 | 登录失败执行—— > 【通过】
\ n "]],"
url ":"
http: //127.0.0.1:8080/users/login/","method":"POST","status_cede":40
1, "response_header": {
"Date": "Thu, 21 Jul 2022 12:42:41
GMT ","
Server ":"
WSGIServer / 0.2 CPython / 3.8 .5 ","
ContentType ":"
application / json ","
WWW - Authenticate ":"
Bearer
realm = \"api\"",
"Vary": "Accept, Origin",
"Allow": "POST, OPTIONS",
"X-FrameOptions": "DENY",
"Content-Length": "37",
"X-Content-TypeOptions": "nosniff",
"Referrer-Policy": "same-origin"
}, "requests_header": {
"UserAgent": "python-requests/2.28.1",
"Accept-Encoding": "gzip,
deflate ","
Accept ":" *
/*","Connection":"keep-alive","customerheader":"wahaha","Content-Length":"44","ContentType":"application/json"},"response_body":"{\n
\"detail\": \"用户名密码错误!
\"\n}","requests_body":"{\n \"username\": \"xinlan\",\n \"password\":
\"123123\"\n}","state":"成功","run_time":"0.345s"}
二、后端代码
创建 testplans/tasks.py 模块,然后编写如下代码:
from projects.models import TestEnv
from testplans.models import TestScene, TestPlan
# from testplans.serializers import TestSceneRunSerializer, TestPlanRunSerializer
# from reports.models import Report, Record
from apitestengine.core.cases import run_test
def _get_env_config(env, debug=True):
"""
获取测试环境的配置数据
:param env: 测试环境对象
:param debug: 是否是调试模式
:return:
"""
# 获取环境变量
var = {**env.global_variable, **env.debug_global_variable} if debug else env.global_variable
ENV = {
**var,
'host': env.host,
'headers': env.headers
}
return {
'ENV': ENV,
'DB': env.db,
'global_func': env.global_func
}
def run_case(case, env_id):
"""执行单条用例"""
# 1. 获取环境数据
env = TestEnv.objects.get(pk=env_id)
config = _get_env_config(env, debug=True)
# 2. 执行用例
res, debug_var = run_test(case_data=[{'Cases': [case]}], env_config=config, debug=True)
# 3. 构造返回结果
result = res['results'][0]['cases'][0]
# 保存一下debug模式下的环境变量
env.debug_global_variable = debug_var
env.save()
return result
# def run_scene(pk, env_id):
# """执行单个的测试场景"""
# # 1. 获取环境配置
# env = TestEnv.objects.get(id=env_id)
# config = _get_env_config(env, debug=True)
# # 2. 构造测试数据
# scene = TestScene.objects.get(id=pk)
# cases = TestSceneRunSerializer(scene).data['scenedata_set']
# # 套件内的用例要排序
# cases.sort(key=lambda x:x['sort'])
# # 组装用例数据
# scene_data = {'Cases': [item['step'] for item in cases], 'name': scene.name}
# # 3. 运行测试用例
# res, debug_var = run_test(case_data=[scene_data], env_config=config, debug=True)
# # 4. 保存一下调试变量
# env.debug_global_variable = debug_var
# env.save()
# # 5. 返回结果
# result = res['results'][0]
# return result
# def run_plan(pk, env_id, record_id):
# """同步执行测试计划"""
# # 1. 获取环境配置
# env = TestEnv.objects.get(id=env_id)
# config = _get_env_config(env, debug=True)
# # 2. 构造测试数据
# # 读取计划任务数据
# plan = TestPlan.objects.get(pk=pk)
# task_data = TestPlanRunSerializer(plan).data
# scene_list = []
# for scene in task_data['scenes']:
# cases = scene['scenedata_set']
# # 排序
# cases.sort(key=lambda x: x['sort'])
# scene_list.append({'Cases': [item['step'] for item in cases], 'name': scene['name']})
# # 3. 运行测试用例
# res = run_test(case_data=scene_list, env_config=config, debug=False)
# # 4. 保存结果
# # 更新测试记录
# record = Record.objects.get(id=record_id)
#
# res['plan'] = plan.id
# res['test_env'] = env_id
# res['tester'] = record.tester
#
# # 保存报告
# Report.objects.create(info=res, record=record)
#
# # 更新record的数据
# record.all = res.get('all', 0)
# record.success = res.get('success', 0)
# record.fail = res.get('fail', 0)
# record.error = res.get('error', 0)
# record.pass_rate = f"{res.get('success', 0) / res.get('all'):.2f}" if res.get('all', 0) else '0'
# # record.pass_rate = ':.2f'.format(res.get('success', 0) / res.get('all')) if res.get('all', 0) else '0'
# record.status = '执行完毕'
# record.save()
视图:
class TestStepViewSet(ModelViewSet):
queryset = TestStep.objects.all()
serializer_class = TestStepSerializer
def get_serializer_class(self):
if self.action == 'retrieve':
return TestStepRetrieveSerializer
return super().get_serializer_class()
@action(methods=['post'], detail=False)
def run(self, request, *args, **kwargs):
# 1. 获取测试数据
cases = request.data.get('data')
env_id = request.data.get('env')
if not env_id:
return Response({'msg': '请求参数env必填', 'data': None}, status=400)
try:
TestEnv.objects.get(id=env_id)
except:
return Response({'msg': '参数env传入的为无效值', 'data': None}, status=400)
res = run_case(cases=cases, env_id=env_id)
return Response(res)
1104

被折叠的 条评论
为什么被折叠?



