前言:
一、测试场景接口
POST 创建测试场景
POST /test_scenes/
Body 请求参数
{
"name": "登录接口测试",
"project": 1
}
请求参数
名称 | 位置 | 类型 | 必选 | 中文名 | 说明 |
body | body | object | 否 | none | |
» name | body | string | 是 | 场景名称 | none |
» project | body | integer | 是 | 所属项目id | none |
返回示例
成功
{
"id": 1,
"name": "登录接口测试",
"project": 1
}
返回结果
状态码 | 状态码含义 | 说明 | 数据模型 |
201 | 成功 | Inline |
返回数据结构
GET 查看测试场景列表
GET /test_scenes/
Body 请求参数
{
"name": "登录接口测试",
"project": 1
}
请求参数
名称 | 位置 | 类型 | 必选 | 中文名 | 说明 |
body | body | object | 否 | none | |
» testplan | body | integer | 是 | 测试计划id | none |
» project | body | integer | 是 | 所属项目id | none |
返回示例
成功
[
{
"id": 1,
"name": "登录接口测试",
"project": 1
},
{
"id": 2,
"name": "注册接口测试",
"project": 1
}
]
返回结果
状态码 | 状态码含义 | 说明 | 数据模型 |
200 | 成功 | Inline |
返回数据结构
DELETE 删除测试场景
DELETE /test_scenes/{pk}/
请求参数
名称 | 位置 | 类型 | 必选 | 中文名 | 说明 |
pk | path | string | 是 | none |
返回示例
204 Response
{}
返回结果
状态码 | 状态码含义 | 说明 | 数据模型 |
204 | 删除成功 | Inline |
返回数据结构
PUT 修改测试场景
PUT /test_scenes/{pk}/
Body 请求参数
{
"name": "登录接口测试",
"project": 1
}
请求参数
名称 | 位置 | 类型 | 必选 | 中文名 | 说明 |
pk | path | string | 是 | none | |
body | body | object | 否 | none | |
» name | body | string | 是 | 场景名称 | none |
» project | body | integer | 是 | 所属项目id | none |
返回示例
成功
{
"id": 1,
"name": "登录接口测试",
"project": 1
}
返回结果
状态码 | 状态码含义 | 说明 | 数据模型 |
200 | 成功 | Inline |
返回数据结构
GET 查看测试场景
GET /test_scenes/{id}/
请求参数
名称 | 位置 | 类型 | 必选 | 中文名 | 说明 |
id | path | string | 是 | none |
返回示例
成功
{
"id": 1,
"name": "登录接口测试",
"project": 1
}
返回结果
状态码 | 状态码含义 | 说明 | 数据模型 |
200 | 成功 | Inline |
二、测试场景步骤接口
POST 测试场景步骤创建
POST /test_scene_steps/
Body 请求参数
{
"sort": 2,
"step": 1,
"scene": 1
}
请求参数
名称 | 位置 | 类型 | 必选 | 中文名 | 说明 |
body | body | object | 否 | none | |
» step | body | integer | 是 | 测试步骤id | none |
» scene | body | integer | 是 | 测试场景id | none |
» sort | body | integer | 是 | 执行顺序 | none |
返回示例
成功
{
"id": 1,
"stepInfo": {
"id": 1,
"title": "登录成功"
},
"sort": 2,
"step": 1,
"scene": 1
}
返回结果
状态码 | 状态码含义 | 说明 | 数据模型 |
201 | 成功 | Inline |
返回数据结构
GET 查看测试场景步骤列表
GET /test_scene_steps/
Body 请求参数
scene: 0
请求参数
名称 | 位置 | 类型 | 必选 | 中文名 | 说明 |
body | body | object | 否 | none | |
» scene | body | integer | 否 | 页码 |
返回示例
成功
[
{
"id": 1,
"stepInfo": {
"id": 1,
"title": "登录成功"
},
"sort": 2,
"step": 1,
"scene": 1
},
{
"id": 1,
"stepInfo": {
"id": 1,
"title": "登录成功"
},
"sort": 2,
"step": 1,
"scene": 1
}
]
返回结果
状态码 | 状态码含义 | 说明 | 数据模型 |
200 | 成功 | Inline |
返回数据结构
DELETE 删除测试场景步骤
DELETE /test_scene_steps/{id}/
请求参数
名称 | 位置 | 类型 | 必选 | 中文名 | 说明 |
id | path | string | 是 | none |
返回示例
204 Response
{}
返回结果
状态码 | 状态码含义 | 说明 | 数据模型 |
204 | 删除成功 | Inline |
返回数据结构
PUT 修改测试场景步骤
PUT /test_scene_steps/{id}/
Body 请求参数
{
"sort": 2,
"step": 1,
"scene": 1
}
请求参数
名称 | 位置 | 类型 | 必选 | 中文名 | 说明 |
id | path | string | 是 | none | |
body | body | object | 否 | none | |
» sort | body | integer | 是 | none | |
» step | body | integer | 是 | none | |
» scene | body | integer | 是 | none |
返回示例
成功
{
"id": 1,
"stepInfo": {
"id": 1,
"title": "登录成功"
},
"sort": 2,
"step": 1,
"scene": 1
}
返回结果
状态码 | 状态码含义 | 说明 | 数据模型 |
201 | 成功 | Inline |
返回数据结构
GET 查看测试场景步骤
GET /test_scene_steps/{id}/
请求参数
名称 | 位置 | 类型 | 必选 | 中文名 | 说明 |
id | path | string | 是 | none |
返回示例
成功
{
"id": 1,
"stepInfo": {
"id": 1,
"title": "登录成功"
},
"sort": 2,
"step": 1,
"scene": 1
}
返回结果
状态码 | 状态码含义 | 说明 | 数据模型 |
200 | 成功 | Inline |
返回数据结构
PUT 测试场景步骤排序
PUT /test_scene_steps/order/
Body 请求参数
[
{
"id": 1,
"stepInfo": {
"id": 1,
"title": "登录成功"
},
"sort": 1,
"step": 1,
"scene": 1
},
{
"id": 2,
"stepInfo": {
"id": 2,
"title": "登录失败"
},
"sort": 2,
"step": 2,
"scene": 1
},
{
"id": 3,
"stepInfo": {
"id": 1,
"title": "登录成功"
},
"sort": 3,
"step": 1,
"scene": 1
},
{
"id": 4,
"stepInfo": {
"id": 2,
"title": "登录失败"
},
"sort": 4,
"step": 2,
"scene": 1
}
]
请求参数
名称 | 位置 | 类型 | 必选 | 中文名 | 说明 |
body | body | object | 否 | none | |
» step | body | integer | 是 | 测试步骤id | |
» scene | body | integer | 是 | 测试场景id | |
» sort | body | integer | 是 | 执行顺序 |
返回示例
成功
[
{
"id": 1,
"stepInfo": {
"id": 1,
"title": "登录成功"
},
"sort": 1,
"step": 1,
"scene": 1
},
{
"id": 2,
"stepInfo": {
"id": 2,
"title": "登录失败"
},
"sort": 2,
"step": 2,
"scene": 1
},
{
"id": 3,
"stepInfo": {
"id": 1,
"title": "登录成功"
},
"sort": 3,
"step": 1,
"scene": 1
},
{
"id": 4,
"stepInfo": {
"id": 2,
"title": "登录失败"
},
"sort": 4,
"step": 2,
"scene": 1
}
]
返回结果
状态码 | 状态码含义 | 说明 | 数据模型 |
200 | 成功 | Inline |
三、后端代码
2.1 模型
class TestScene(models.Model):
"""测试场景/测试套件"""
project = models.ForeignKey('projects.Project', help_text='所属项目', verbose_name='项目名称',
on_delete=models.PROTECT, related_name='test_scenes')
name = models.CharField(max_length=50, help_text='测试场景名', verbose_name='测试场景名')
def __str__(self):
return self.name
class Meta:
db_table = 'tb_test_scene'
verbose_name = "测试场景"
verbose_name_plural = verbose_name
class SceneData(models.Model):
"""场景/套件数据"""
step = models.ForeignKey('TestStep', help_text='步骤', verbose_name='步骤', on_delete=models.PROTECT)
scene = models.ForeignKey('TestScene', help_text='场景', verbose_name='场景', on_delete=models.PROTECT)
sort = models.IntegerField(help_text='执行顺序', verbose_name='执行顺序', blank=True)
def __str__(self):
return str(self.id)
class Meta:
db_table = 'tb_scene_data'
verbose_name = "场景步骤"
verbose_name_plural = verbose_name
2.2 序列化器
class TestSceneSerializer(ModelSerializer):
"""测试场景序列化器"""
class Meta:
model = TestScene
fields = '__all__'
class TestSceneStepSerializer(ModelSerializer):
"""测试场景步骤序列化器"""
stepInfo = NestTestStepSerializer(read_only=True, source='step')
class Meta:
model = SceneData
fields = '__all__'
2.3 视图
class TestSceneViewSet(ModelViewSet):
"""测试场景视图集"""
queryset = TestScene.objects.all()
serializer_class = TestSceneSerializer
# filterset_fields = ['testplan', 'project']
permission_classes = [IsAuthenticated]
@action(methods=['post'], detail=True)
def run(self, request, pk):
# 1. 获取参数
env_id = request.data.get('env')
if not env_id:
return Response({'msg': '请求参数env必填'}, status=400)
try:
TestEnv.objects.get(id = env_id)
except:
return Response({'msg': '传入的env无效'}, status=400)
# 2. 执行测试场景
res = run_scene(pk, env_id)
# 3. 返回响应
return Response(res)
class TestSceneStepViewSet(ModelViewSet):
"""测试场景--步骤的视图集"""
queryset = SceneData.objects.all()
serializer_class = TestSceneStepSerializer
filterset_fields = ['scene']
permission_classes = [IsAuthenticated]
# 视图集,默认的5个方法,对应5个接口
# 给视图集添加额外的action,额外的方法,提供更多的接口
# 通过action装饰器,装饰其他方法后,可以将其他的方法增加为当前视图集的接口
# 默认生成的路由是在原有url后面拼接上当前方法名
# action的第一个参数是一个列表,表示这个接口需要响应的http请求的http方法
# action装饰器中的url_path参数可以指定url,覆盖默认行为
# action装饰器,还有一个参数detail必填,
# 如果是要操作单个的对象,detail=True,那么路由中就需要有pk /order/<pk>/
# 如果操作的是查询集,detail=False,路由中就不能有pk
@action(['put'], detail=False)
def order(self, request, format=None): # 会自动生成路由,以方法名作为url
# 循环传递过来的每一个场景步骤对象
for item in request.data:
obj = SceneData.objects.get(pk=item['id'])
obj.sort = item['sort']
obj.save(update_fields=['sort'])
return Response(request.data)
2.3.1 action扩展
测试场景步骤排序接口和测试场景步骤强相关,单独创建一个视图会有很多重复代码,且逻辑接口不严谨。如果能把 这个接口也定义在测试场景步骤视图集中则更加的简介和符合逻辑。
drf
中的视图集除了标准的请求处理方法外,还可以定义额外的处理方法,并通过 action 装饰器
自动添加路由。 在测试场景步骤视图集中添加如下方法:
@action(methods=['put'], detail=False)
def order(self, request, format=None):
...
与标准处理方法一样,额外定义的方法可以针对单个对象或整个集合。参数 detail
设置为True时表示针对单个对 象,这时生成的路由中会包含 pk
。
action
装饰器默认路由请求为 GET
,也可以通过设置 methods
参数接受其他HTTP方法。 也可以覆盖任何视图级别的配置,例如 permisson_classes , srializer_class , filter_backends 等等:
@action(methods=['POST'], detail=True, permission_classes=[IsAuthenticated])
def run(self, requst, pk):
这个操作将生成路由 ^/test_scene_steps/order/$ 。
2.4 路由
from rest_framework.routers import DefaultRouter
from .views import TestStepViewSet, UploadFileViewSet, TestSceneViewSet, TestSceneStepViewSet
route = DefaultRouter()
route.register('test_steps', TestStepViewSet)
route.register('upload', UploadFileViewSet)
route.register('test_scenes', TestSceneViewSet)
route.register('test_scene_steps', TestSceneStepViewSet)
urlpatterns = route.urls