> 文章列表 > 三十二、实战演练之接口自动化平台的模型创建、项目管理接口设计

三十二、实战演练之接口自动化平台的模型创建、项目管理接口设计

三十二、实战演练之接口自动化平台的模型创建、项目管理接口设计

1. 模型编写

根据项目需求分析和数据库设计,我们创建项目应用,测试计划应用,测试报告应用,bug应用:

django-admin startapp projects
django-admin startapp testplans
django-admin startapp reports
django-admin startapp bugs

注意在配置文件中注册应用。然后分别创建对应的模型如下:

(1)项目应用模型

from django.db import models
from utils.models import BaseModelclass Project(models.Model):"""项目表"""name = models.CharField(max_length=50, help_text='项目名称', verbose_name='项目名')leader = models.CharField(max_length=50, help_text='负责人', verbose_name='负责人', default='')create_time = models.DateTimeField(verbose_name='创建时间', help_text='创建时间', auto_now_add=True)def __str__(self):return self.nameclass Meta:db_table = 'project'verbose_name = "项目表"verbose_name_plural = verbose_nameclass TestEnv(models.Model):"""测试环境表"""name = models.CharField(max_length=150, help_text='环境名称', verbose_name='环境名称')# 一个项目有多个测试环境,一个环境只属于一个项目# models.CASCADE 删除项目的时候,就把相关环境同步删除。project = models.ForeignKey(Project, on_delete=models.CASCADE, help_text='项目id', verbose_name='项目id',related_name='test_envs')  # 反向字段名global_variable = models.JSONField(help_text='全局变量', verbose_name='全局变量', default=dict, null=True,blank=True)debug_global_variable = models.JSONField(help_text='debug模式全局变量', verbose_name='debug模式全局变量',default=dict, null=True, blank=True)db = models.JSONField(help_text='数据库配置', verbose_name='数据库配置', default=list, null=True, blank=True)host = models.CharField(help_text='base_url地址', verbose_name='base_url地址', max_length=100, blank=True)headers = models.JSONField(help_text='请求头', verbose_name='请求头', default=dict, null=True, blank=True)global_func = models.TextField(help_text='用例工具文件', verbose_name='用例工具文件',default=open('utils/global_func.py', 'r', encoding='utf-8').read(), null=True,blank=True)def __str__(self):return self.nameclass Meta:db_table = 'tb_test_env'verbose_name = "测试环境表"verbose_name_plural = verbose_nameclass Interface(models.Model):"""接口表"""CHOICES = [('1', '项目接口'),('2', '外部接口')]project = models.ForeignKey(Project, on_delete=models.CASCADE, help_text='项目id',verbose_name='项目id',related_name='interfaces')name = models.CharField(max_length=50, help_text='接口名称', verbose_name='接口名')url = models.CharField(max_length=200, help_text='接口路径', verbose_name='接口路径')method = models.CharField(max_length=50, help_text='请求方法', verbose_name='请求方法')type = models.CharField(verbose_name='接口类型', help_text='接口类型',max_length=40, choices=CHOICES, default='1')def __str__(self):return self.urlclass Meta:db_table = 'tb_interface'verbose_name = '接口表'verbose_name_plural = verbose_name

(2)bug应用模型

from django.db import modelsclass Bug(models.Model):"""Bug模型"""CHOICES = [('未处理', '未处理'),('处理中', '处理中'),('处理完', '处理完'),('无效bug', '无效bug'),]# 项目project = models.ForeignKey('projects.Project', on_delete=models.CASCADE, help_text='项目id', verbose_name='项目id')# 接口interface = models.ForeignKey('projects.Interface', on_delete=models.CASCADE, verbose_name='接口', help_text='接口')# bug描述desc = models.TextField(verbose_name='bug描述', help_text='bug描述', max_length='3000', blank=True)# bug基本信息 (请求头 请求体 请求方式 响应结果)info = models.JSONField(verbose_name='bug信息', help_text='bug信息', default=dict, blank=True)# bug状态 (待提交,已提交,处理中,关闭,无效bug)status = models.CharField(verbose_name='bug状态', help_text='bug状态', max_length=40, choices=CHOICES,default='未处理')# bug提交者user = models.CharField(verbose_name='提交者', help_text='提交者', max_length=40, default='', blank=True)create_time = models.DateTimeField(verbose_name='创建时间', help_text='创建时间', auto_now_add=True)class Meta:db_table = 'tb_bug'verbose_name = 'bug表'verbose_name_plural = verbose_namedef __str__(self):return self.idclass BugHandle(models.Model):"""bug操作记录表"""bug = models.ForeignKey('Bug', on_delete=models.CASCADE, verbose_name='bug_id', help_text='bug_id')create_time = models.DateTimeField(verbose_name='创建时间', help_text='创建时间', auto_now_add=True)handle = models.TextField(verbose_name='处理操作', help_text='处理操作', blank=True)update_user = models.CharField(verbose_name='更新用户', help_text='更新用户', max_length=32, blank=True)class Meta:db_table = 'tb_bug_handle'verbose_name = 'bug操作记录表'verbose_name_plural = verbose_name

(3)测试报告应用模型

from django.db import modelsclass Record(models.Model):"""运行记录表"""create_time = models.DateTimeField(verbose_name='创建时间', help_text='创建时间', auto_now_add=True)plan = models.ForeignKey('testplans.TestPlan', help_text='执行计划', verbose_name='执行计划',on_delete=models.PROTECT, related_name='records')all = models.IntegerField(help_text='用例总数', verbose_name='用例总数', blank=True, default=0)success = models.IntegerField(help_text='成功用例', verbose_name='成功用例', blank=True, default=0)fail = models.IntegerField(help_text='失败用例', verbose_name='失败用例', blank=True, default=0)error = models.IntegerField(help_text='错误用例', verbose_name='错误用例', blank=True, default=0)pass_rate = models.CharField(help_text='执行通过率', verbose_name='执行通过率', max_length=100, blank=True,default=0)tester = models.CharField(help_text='执行者', verbose_name='执行者', max_length=100, blank=True)test_env = models.ForeignKey('projects.TestEnv', help_text='测试环境', verbose_name='测试环境',on_delete=models.PROTECT)statue = models.CharField(help_text='执行状态', verbose_name='执行状态', max_length=100)def __str__(self):return str(self.id)class Meta:db_table = 'tb_record'verbose_name = '运行记录表'verbose_name_plural = verbose_nameclass Report(models.Model):"""测试报告"""info = models.JSONField(help_text='测试报告', verbose_name='测试报告', default=dict, blank=True)record = models.OneToOneField('Record', help_text='测试记录', verbose_name='测试记录 ', on_delete=models.PROTECT)def __str__(self):return str(self.id)class Meta:db_table = 'tb_report'verbose_name = '报告表'verbose_name_plural = verbose_name

(4)测试计划应用模型

from django.db import modelsclass TestPlan(models.Model):"""测试计划"""create_time = models.DateTimeField(verbose_name='创建时间', help_text='创建时间', auto_now_add=True)name = models.CharField(verbose_name='计划名', help_text='计划名', max_length=150)project = models.ForeignKey('projects.Project', verbose_name='项目id', help_text='项目id', on_delete=models.CASCADE,related_name='test_plan')scenes = models.ManyToManyField('TestScene', help_text='包含的测试场景', verbose_name='包含的测试场景', blank=True)def __str__(self):return self.nameclass Meta:db_table = 'tb_test_plan'verbose_name = '测试计划表'verbose_name_plural = verbose_nameclass 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.nameclass Meta:db_table = 'tb_test_scene'verbose_name = "测试场景"verbose_name_plural = verbose_nameclass 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_namesetup_script = """
# 前置脚本(python):
# global_tools:全局工具函数
# data:用例数据
# env: 局部环境# ENV: 全局环境
# db: 数据库操作对象
"""
teardown_script = """
# 后置脚本(python):
# global_tools:全局工具函数
# data:用例数据
# response:响应对象response
# env: 局部环境
# ENV: 全局环境
# db: 数据库操作对象
"""class TestStep(models.Model):"""用例表"""title = models.CharField(max_length=50, help_text='用例名称', verbose_name='用例名')interface = models.ForeignKey('projects.Interface', on_delete=models.CASCADE, help_text='接口', verbose_name='接口')headers = models.JSONField(help_text='请求头', verbose_name='请求头', default=dict, blank=True)request = models.JSONField(help_text='请求信息', verbose_name='请求信息', default=dict, blank=True)file = models.JSONField(help_text='上传的文件参数', verbose_name='上传的文件', default=list, blank=True)setup_script = models.TextField(help_text='前置脚本', verbose_name='前置脚本', default=setup_script, blank=True)teardown_script = models.TextField(help_text='后置脚本', verbose_name='用例后置脚本 ', default=teardown_script,blank=True)def __str__(self):return self.titleclass Meta:db_table = 'tb_test_step'verbose_name = "测试步骤表"verbose_name_plural = verbose_name

二、项目管理接口设计

(1)项目创建

接口名称: /projects/

请求方式: POST

参数格式: JSON

请求参数:

参数

变量名

类型

说明

是否必传

项目名称

name

字符串

项目名称

项目负责人

leader

字符串

项目负责人

请求示例:

json格式参数

{"name": "前程贷","leader": "心蓝"}

返回示例

响应状态码:201 响应数据:

{"id": 1,"create_time": "2022-07-12T21:59:57.606147+08:00","name": "ck12接口自动化","leader": "心蓝"}

(2)删除项目

接口名称: /projects/项目ID/

请求方式: DELETE

参数格式: 路径参数

请求参数:

返回示例

响应状态码:204

响应数据:无

(3)修改项目

接口名称: /projects/项目ID/

请求方式: PUT/PATCH

参数格式: JSON

请求参数:

参数

变量名

类型

说明

是否必传

项目名称

name

字符串

项目名称

PUT请求必传

项目负责人

leader

字符串

项目负责人

PUT请求必传

请求示例:

json格式参数

{"name": "前程贷","leader": "心蓝"}

返回示例

响应状态码:200 响应数据:

{"id": 1,"create_time": "2022-07-12T21:59:57.606147+08:00","name": "ck12接口自动化","leader": "心蓝"}

(4)查看项目列表

接口名称:

请求方式: GET

参数格式: 无

请求参数: 无

返回示例

响应状态码:200 响应数据:

[{id": 1,"create_time": "2022-07-12T21:59:57.606147+08:00","name": "ck12接口自动化","leader": "心蓝"}]

(5)查看项目

接口名称:

请求方式: GET

参数格式: 路径参数

请求参数:

返回示例

响应状态码:200 响应数据:

{"id": 1,"create_time": "2022-07-12T21:59:57.606147+08:00","name": "ck12接口自动化","leader": "心蓝","info": [{"name": "执行环境","value": 0},{"name": "测试场景","value": 0 	},{"name": "测试计划","value": 0 	},{"name": "接口数量","value": 0 	},{"name": "定时任务","value":0},{"name": "执行记录","value": 0}],"bugs":[{"name": "未处理bug","value": 0},{"name": "处理中bug","value": 0},{"name": "处理完bug","value": 0},{"name": "无效bug","value": 0}]

(6)后端代码

① 新增模型方法

因为在获取项目详情时需要统计对应的数据,所以我们再项目模型中新增如下方法:

class Project(models.Model):"""项目表"""name = models.CharField(verbose_name="项目名", help_text="项目名称", max_length=50)leader = models.CharField(verbose_name="负责人", help_text="项目负责人", max_length=50, default='')create_time = models.DateTimeField(verbose_name="创建时间", help_text="", auto_now_add=True)def __str__(self):return self.nameclass Meta:db_table = "project"verbose_name = "项目表"verbose_name_plural = verbose_namedef info(self):"""返回项目的统计信息"""return [{'name': '执行环境', 'value': self.test_envs.count()},{'name': '测试场景', 'value': self.test_scenes.count()},{'name': '测试计划', 'value': self.test_plan.count()},{'name': '接口数量', 'value': self.interfaces.count()},{'name': '定时任务', 'value': 0},{'name': '执行记录', 'value': Record.objects.filter(plan__name=self.name).count()},]def bugs(self):"""返回bugs的统计信息"""return [{'name': '未处理', 'value': self.bug_set.filter(status='未处理').count()},{'name': '处理中', 'value': self.bug_set.filter(status='处理中').count()},{'name': '处理完', 'value': self.bug_set.filter(status='处理完').count()},{'name': '无效bug', 'value': self.bug_set.filter(status='无效bug').count()},]

② 序列化器

在projects应用中创建serializers.py 模块,编写代码如下:

class ProjectSerializer(serializers.ModelSerializer):"""项目序列化器"""# 注意info和bugs是对象方法class Meta:model = Projectfields = ['id', 'create_time', 'name', 'leader', 'info', 'bugs']

③ 视图

class ProjectViewSet(ModelViewSet):"""项目视图集"""serializer_class = ProjectSerializerqueryset = Project.objects.all()permission_classes = [IsAuthenticated, IsLeaderOrReadOnly]# 还可以过滤一下查询集,实现特定的人群看到特定的项目def get_queryset(self):return Project.objects.filter(leader=self.request.user)

④ 路由

from rest_framework.routers import DefaultRouterfrom . import viewsrouter = DefaultRouter()
router.register('projects', views.ProjectViewSet)
urlpatterns = router.urls