自动化测试平台开发(六):接口测试 - 数据表设计
本篇开始接口测试部分详细开发。
1. 数据表设计
django ORM设计、处理数据表,表字段内容后期空闲了再更新。。。
- BaseModel - 基础表
- 部门表
- 项目表
- 项目动态
- 项目成员
- 接口分组
- 接口表
- 接口更新历史
- YAPI事件表
- 全局环境配置
- 全局变量
- 全局请求头
- 全局标签
- 全局校验规则
- 测试用例集
- 测试用例表
- 测试步骤表
- 测试步骤表
- 任务
2. 附源码
1 import json 2 from django.utils import timezone 3 from django.db import models 4 from django.core.validators import validate_comma_separated_integer_list 5 from django.contrib.auth.models import User 6 from rest_framework.authtoken.models import Token 7 8 # 测试结果选项 9 RESULT_CHOICE = ( 10 ('passed', '成功'), 11 ('failed', '失败'), 12 ('skipped', '跳过'), 13 ('error', '故障'), 14 ('', '未执行'), 15 ) 16 # 接口变更处理进度选项 17 API_UPDATE_STATUS_CHOICE = ( 18 (0, '待处理'), 19 (1, '待验证'), 20 (2, '已处理') 21 ) 22 # 测试构建类型选项 23 BUILD_TYPE_CHOICE = ( 24 ('环境验证', '环境验证'), 25 ('冒烟测试', '冒烟测试'), 26 ('业务巡检', '业务巡检'), 27 ('其他', '其他') 28 ) 29 30 31 class SoftDelTableQuerySet(models.QuerySet): 32 def delete(self): 33 self.update(is_delete=True, delete_time=timezone.now()) 34 35 36 class BaseManager(models.Manager): 37 _queryset_class = SoftDelTableQuerySet 38 39 def get_queryset(self): 40 return super().get_queryset().filter(is_delete=False) 41 42 43 # 基础表:公共字段列 - 创建时间/更新时间/状态/描述 44 class BaseModel(models.Model): 45 """ 46 公共字段列 47 """ 48 49 create_time = models.DateTimeField(verbose_name='创建时间', auto_now_add=True) 50 update_time = models.DateTimeField(verbose_name='更新时间', null=True, auto_now=True) 51 delete_time = models.DateTimeField(verbose_name="删除时间", null=True, default=None) 52 is_delete = models.BooleanField(verbose_name='是否已删除', default=False) 53 54 # creator = models.CharField(verbose_name="创建人", max_length=20, null=True) 55 # updater = models.CharField(verbose_name="更新人", max_length=20, null=True) 56 status = models.BooleanField(default=True, verbose_name='状态(1正常 0停用)') 57 description = models.CharField(max_length=4096, blank=True, null=True, verbose_name='描述') 58 59 def delete(self, using=None, keep_parents=False): 60 self.is_delete = True 61 self.delete_time = timezone.now() 62 self.save() 63 64 objects = BaseManager() 65 66 class Meta: 67 abstract = True # 抽象基类 68 verbose_name = "公共字段表" 69 db_table = 'base_table' 70 71 72 # 字典类型表 - 灵活配置、kv存储 73 class DictType(models.Model): 74 id = models.AutoField(primary_key=True) 75 name = models.CharField(max_length=100, default='', verbose_name='字典名称') 76 type = models.CharField(max_length=100, default='', verbose_name='字典类型') 77 remark = models.CharField(max_length=500, default='', verbose_name='备注') 78 79 creator = models.ForeignKey(User, on_delete=models.SET_NULL, null=True, max_length=20, 80 verbose_name="创建人", related_name="dict_type_creator") 81 updater = models.ForeignKey(User, on_delete=models.SET_NULL, null=True, max_length=20, 82 verbose_name="更新人", related_name="dict_type_updater") 83 84 def __unicode__(self): 85 return self.name 86 87 def __str__(self): 88 return self.name 89 90 class Meta: 91 verbose_name = '字典类型表' 92 verbose_name_plural = '字典类型表' 93 94 95 # 字典数据表 - 灵活配置、kv存储 96 class DictData(BaseModel): 97 id = models.AutoField(primary_key=True) 98 dict_type = models.ForeignKey(DictType, on_delete=models.SET_NULL, null=True, max_length=50, 99 verbose_name="字典名称", related_name="data_dict") 100 dict_sort = models.CharField(max_length=100, default='', verbose_name='字典排序') 101 dict_label = models.CharField(max_length=100, default='', verbose_name='字典标签') 102 dict_value = models.CharField(max_length=100, default='', verbose_name='字典键值') 103 list_class = models.CharField(max_length=100, default='', verbose_name='表格回显样式(success/info/warning/danger)') 104 is_default = models.BooleanField(default=False, verbose_name='是否默认(1是 0否)') 105 remark = models.CharField(max_length=500, default='', verbose_name='备注') 106 107 creator = models.ForeignKey(User, on_delete=models.SET_NULL, null=True, max_length=20, 108 verbose_name="创建人", related_name="dict_data_creator") 109 updater = models.ForeignKey(User, on_delete=models.SET_NULL, null=True, max_length=20, 110 verbose_name="更新人", related_name="dict_data_updater") 111 112 def __unicode__(self): 113 return self.dict_type.type 114 115 def __str__(self): 116 return self.dict_type.type 117 118 class Meta: 119 verbose_name = '字典数据表' 120 verbose_name_plural = '字典数据表' 121 122 123 # 部门表 124 class Department(BaseModel): 125 id = models.AutoField(primary_key=True) 126 name = models.CharField(max_length=50, verbose_name='部门名称') 127 safe_name = models.SlugField(default='', blank=True, null=True, max_length=50, verbose_name='部门标识(用作文件夹名)') 128 leader = models.CharField(default='', blank=True, null=True, max_length=50, verbose_name='负责人') 129 130 def __unicode__(self): 131 return self.name 132 133 def __str__(self): 134 return self.name 135 136 class Meta: 137 verbose_name = '部门' 138 verbose_name_plural = '部门' 139 140 141 # 项目表 142 class Project(BaseModel): 143 """项目表""" 144 id = models.AutoField(primary_key=True) 145 name = models.CharField(verbose_name='项目名称', max_length=50) 146 version = models.CharField(verbose_name='版本', max_length=50, null=True) 147 department = models.ForeignKey(Department, on_delete=models.CASCADE, null=True, verbose_name='部门') 148 creator = models.ForeignKey(User, on_delete=models.SET_NULL, null=True, max_length=20, 149 verbose_name="创建人", related_name="project_creator") 150 updater = models.ForeignKey(User, on_delete=models.SET_NULL, null=True, max_length=20, 151 verbose_name="更新人", related_name="project_updater") 152 153 def __unicode__(self): 154 return self.name 155 156 def __str__(self): 157 return self.name 158 159 class Meta: 160 verbose_name = '项目' 161 verbose_name_plural = '项目' 162 163 164 # 项目动态 --TODO 165 class ProjectDynamic(models.Model): 166 """项目动态""" 167 id = models.AutoField(primary_key=True) 168 project = models.ForeignKey(Project, related_name='dynamic_project', on_delete=models.CASCADE, verbose_name='所属项目') 169 time = models.DateTimeField(verbose_name='操作时间', max_length=128) 170 type = models.CharField(verbose_name='操作类型', max_length=50) 171 operationObject = models.CharField(verbose_name='操作对象', max_length=50) 172 user = models.ForeignKey(User, blank=True, null=True, related_name='userName', 173 on_delete=models.SET_NULL, verbose_name='操作人') 174 description = models.CharField(max_length=1024, blank=True, null=True, verbose_name='描述') 175 176 def __unicode__(self): 177 return self.type 178 179 class Meta: 180 verbose_name = '项目动态' 181 verbose_name_plural = '项目动态' 182 183 184 # 项目成员 185 class ProjectMember(models.Model): 186 """""" 187 CHOICES = ( 188 ('超级管理员', '超级管理员'), 189 ('开发人员', '开发人员'), 190 ('测试人员', '测试人员'), 191 ('测试开发', '测试开发'), 192 ('游客', '游客') 193 ) 194 id = models.AutoField(primary_key=True) 195 role = models.CharField(max_length=50, verbose_name='角色', choices=CHOICES) 196 status = models.BooleanField(default=True, verbose_name='状态') 197 project = models.ForeignKey(Project, related_name='member_project', on_delete=models.CASCADE, verbose_name='所属项目') 198 user = models.ForeignKey(User, related_name='member_user', on_delete=models.CASCADE, verbose_name='用户') 199 200 def __unicode__(self): 201 return self.role 202 203 def __str__(self): 204 return self.role 205 206 class Meta: 207 verbose_name = '项目成员' 208 verbose_name_plural = '项目成员' 209 210 211 # app系统配置 212 def default_app_setting(): 213 setting = { 214 "debug": {"value": True, "description": "Debug模式"}, 215 "file_log_level": {"value": 'DEBUG', "description": "日志等级(文件)"}, 216 "console_log_level": {"value": 'INFO', "description": "日志等级(控制台)"}, 217 "testcase_max_rotation": {"value": 50, "description": "保留历史构建数据的最大个数"} 218 } 219 return setting 220 221 222 class AppSetting(models.Model): 223 """app系统配置,如debug模式、构建保留数等""" 224 id = models.AutoField(primary_key=True) 225 name = models.CharField(max_length=50, null=True, default='app系统配置', verbose_name='Name') 226 data = models.JSONField(default=default_app_setting, verbose_name='APP配置数据') 227 description = models.CharField(max_length=1024, blank=True, null=True, verbose_name='描述') 228 status = models.BooleanField(default=True, verbose_name='状态') 229 230 def delete(self, using=None, keep_parents=False): 231 return "app系统配置,禁止删除!" 232 233 def __unicode__(self): 234 return self.name 235 236 def __str__(self): 237 return self.name 238 239 def get_default_app_setting(self): 240 return default_app_setting() 241 242 class Meta: 243 verbose_name = 'app系统配置' 244 verbose_name_plural = 'app系统配置' 245 246 247 # 全局环境配置 248 def default_env_config(): 249 config = { 250 "company_id": {"value": '', "description": "公司ID"}, 251 "base_url_mk": {"value": '', "description": "服务商后台地址"}, 252 "base_url_qw": {"value": '', "description": "企微端地址"}, 253 "base_url_oss_bill": {"value": '', "description": "运营计费地址"}, 254 "base_url_oss_official": {"value": '', "description": "运营官方地址"}, 255 "base_url_qyapi": {"value": 'https://qyapi.weixin.qq.com', "description": "企业微信地址"}, 256 "corp_id": {"value": '', "description": "企微企业标识corp_id"} 257 } 258 return config 259 260 261 def default_qw_external_contact_config(): 262 cf = { 263 "external_contact_token": {"value": '', "description": "企微客户联系Token"}, 264 "external_contact_aes_key": {"value": '', "description": "企微客户联系AESKey"}, 265 "external_contact_corp_secret": {"value": '', "description": "企微客户联系Secret"}, 266 } 267 return cf 268 269 270 class GlobalEnv(models.Model): 271 """ 272 测试环境配置 environment 273 """ 274 id = models.AutoField(primary_key=True) 275 name = models.CharField(max_length=50, verbose_name='名称') 276 config = models.JSONField(default=default_env_config, verbose_name='环境基础配置') 277 qw_external_contact_config = models.JSONField(default=default_qw_external_contact_config, verbose_name='企微客户联系配置') 278 data = models.JSONField(default=dict, verbose_name='环境数据') 279 mock = models.JSONField(default=dict, verbose_name='环境mock数据') 280 mock_dynamic = models.BooleanField("动态更新mock", default=True) 281 description = models.CharField(max_length=1024, blank=True, null=True, verbose_name='描述') 282 status = models.BooleanField(default=True, verbose_name='状态') 283 is_default = models.BooleanField("默认配置", default=False) 284 285 def __unicode__(self): 286 return self.name 287 288 def __str__(self): 289 return self.name 290 291 def get_default_config(self): 292 return default_env_config() 293 294 def get_default_qw_external_contact_config(self): 295 return default_qw_external_contact_config() 296 297 class Meta: 298 verbose_name = '环境配置' 299 verbose_name_plural = '环境配置管理' 300 301 302 # 全局const 303 class GlobalConst(models.Model): 304 """全局const""" 305 id = models.AutoField(primary_key=True) 306 name = models.CharField(max_length=50, verbose_name='Name') 307 value = models.TextField(blank=True, null=True, verbose_name='Value') 308 description = models.CharField(max_length=1024, blank=True, null=True, verbose_name='描述') 309 status = models.BooleanField(default=True, verbose_name='状态') 310 311 def __unicode__(self): 312 return self.name 313 314 def __str__(self): 315 return self.name 316 317 class Meta: 318 verbose_name = 'Const' 319 verbose_name_plural = '全局Const管理' 320 321 322 # 全局Header 323 class GlobalHeader(models.Model): 324 id = models.AutoField(primary_key=True) 325 name = models.CharField(max_length=1024, verbose_name="名称") 326 value = models.TextField(blank=True, null=True, verbose_name='内容') 327 description = models.CharField(max_length=1024, blank=True, null=True, verbose_name='描述') 328 status = models.BooleanField(default=True, verbose_name='状态') 329 330 def __unicode__(self): 331 return self.name 332 333 def __str__(self): 334 return self.name 335 336 class Meta: 337 verbose_name = '全局请求头' 338 verbose_name_plural = '全局请求头管理' 339 340 341 # 通用校验规则配置 342 class GlobalResponseValidate(models.Model): 343 id = models.AutoField(primary_key=True) 344 name = models.CharField(max_length=1024, default='默认校验', verbose_name="名称") 345 description = models.CharField(max_length=1024, blank=True, null=True, verbose_name='描述') 346 check_status_code = models.BooleanField(default=True, verbose_name='检查状态代码') 347 check_json_schema = models.BooleanField(default=True, verbose_name='检查json-schema') 348 check_response_data = models.BooleanField(default=True, verbose_name='检查响应数据') 349 status_code = models.CharField(default='200', max_length=500, verbose_name='期望状态代码', 350 validators=[validate_comma_separated_integer_list]) 351 status = models.BooleanField(default=True, verbose_name='状态') 352 is_default = models.BooleanField("默认配置", default=False) 353 354 def save(self, *args, **kwargs): 355 if self.is_default: 356 try: 357 GlobalResponseValidate.objects.filter(is_default=True).update(is_default=False) 358 except GlobalResponseValidate.DoesNotExist: 359 pass 360 super(GlobalResponseValidate, self).save(*args, **kwargs) 361 362 def __unicode__(self): 363 return self.name 364 365 def __str__(self): 366 return self.name 367 368 class Meta: 369 verbose_name = '通用校验规则配置' 370 verbose_name_plural = '通用校验规则配置管理' 371 372 373 # 全局标签 374 class GlobalLabel(models.Model): 375 """ 376 全局标签 377 """ 378 LABEL_TYPE_CHOICE = ( 379 ('priority', '优先级'), # 如 P0、P1、P2 380 ('severity', '严重等级'), # 如 normal、blocker 381 ('function', '业务功能'), # 如 登录、权限检查 382 ('testsuite_type', '测试集类型'), # 如 冒烟、巡检、环境验证 383 ('other', '其他'), 384 ) 385 id = models.AutoField(primary_key=True) 386 name = models.CharField(max_length=50, verbose_name='标签名') 387 type = models.CharField(verbose_name='标签类型', choices=LABEL_TYPE_CHOICE, default='priority', max_length=20) 388 status = models.BooleanField(default=True, verbose_name='状态') 389 description = models.CharField(max_length=1024, blank=True, null=True, verbose_name='描述') 390 391 def __unicode__(self): 392 return self.name 393 394 def __str__(self): 395 return self.name 396 397 class Meta: 398 verbose_name = '标签' 399 verbose_name_plural = '标签管理' 400 401 402 # 自定义方法 --TODO 403 class CustomMethod(models.Model): 404 """ 405 自定义方法 406 """ 407 id = models.AutoField(primary_key=True) 408 project = models.ForeignKey(Project, on_delete=models.CASCADE, verbose_name='项目') 409 name = models.CharField(max_length=50, verbose_name='方法名') 410 description = models.CharField(max_length=1024, blank=True, null=True, verbose_name='描述') 411 type = models.CharField(max_length=50, verbose_name='类型') 412 dataCode = models.TextField(verbose_name='代码') 413 status = models.BooleanField(default=True, verbose_name='状态') 414 415 def __unicode__(self): 416 return self.name 417 418 class Meta: 419 verbose_name = '自定义方法' 420 verbose_name_plural = '自定义方法' 421 422 423 # 接口分组 424 class ApiGroup(BaseModel): 425 """ 426 接口分组 427 """ 428 id = models.AutoField(primary_key=True) 429 name = models.CharField(max_length=50, verbose_name='接口分组名称') 430 project = models.ForeignKey(Project, on_delete=models.CASCADE, verbose_name='项目') 431 432 def __unicode__(self): 433 return self.name 434 435 def __str__(self): 436 return self.name 437 438 class Meta: 439 verbose_name = '接口分组' 440 verbose_name_plural = '接口分组' 441 442 443 # 接口信息 444 class ApiInfo(BaseModel): 445 """ 446 接口信息 447 """ 448 HTTP_CHOICE = ( 449 ('HTTP', 'HTTP'), 450 ('HTTPS', 'HTTPS') 451 ) 452 REQUEST_TYPE_CHOICE = ( 453 ('POST', 'POST'), 454 ('GET', 'GET'), 455 ('PUT', 'PUT'), 456 ('DELETE', 'DELETE'), 457 ('CALL', 'CALL') 458 ) 459 HOST_TAG_CHOICE = ( 460 ('mk', '服务商后台地址'), 461 ('qw', '企微端地址'), 462 ('oss_bill', '运营计费地址'), 463 ('oss_official', '运营官方地址'), 464 ('qyapi', '企业微信API'), 465 ) 466 # 接口数据来源,默认manual - 手动添加 467 ORIGIN_CHOICE = ( 468 ('yapi', 'yapi'), 469 ('xmind', 'xmind'), 470 ('excel', 'excel'), 471 ('manual', 'manual'), 472 ) 473 474 id = models.AutoField(primary_key=True) 475 yapi_id = models.IntegerField(default=0, verbose_name="YAPI接口ID", ) 476 project = models.ForeignKey(Project, related_name='api_project', null=True, on_delete=models.CASCADE, 477 verbose_name='所属项目') 478 api_group = models.ForeignKey(ApiGroup, blank=True, null=True, related_name='api_group', 479 on_delete=models.SET_NULL, verbose_name='接口分组') 480 origin = models.CharField(max_length=50, default='manual', verbose_name='接口数据来源', choices=ORIGIN_CHOICE) 481 name = models.CharField(max_length=500, verbose_name='接口名称') 482 http_type = models.CharField(max_length=50, default='HTTP', verbose_name='HTTP/HTTPS', choices=HTTP_CHOICE) 483 host_tag = models.CharField(max_length=30, verbose_name='指定host', default='mk', choices=HOST_TAG_CHOICE) 484 method = models.CharField(max_length=50, verbose_name='请求方式', choices=REQUEST_TYPE_CHOICE) 485 path = models.CharField(max_length=1024, verbose_name='接口地址') 486 # yapi定义参数 - diff 487 yapi_req_headers = models.TextField(blank=True, null=True, verbose_name='yapi定义-请求头') # json字符串,diff 488 yapi_req_params = models.TextField(blank=True, null=True, verbose_name='yapi定义-请求参数-params') # json字符串,diff 489 yapi_req_query = models.TextField(blank=True, null=True, verbose_name='yapi定义-请求参数-query') # json字符串,diff 490 yapi_req_body_form = models.TextField(blank=True, null=True, verbose_name='yapi定义-请求参数-body_form') # json字符串,diff 491 yapi_req_body_other = models.TextField(blank=True, null=True, verbose_name='yapi定义-请求参数-body_other') # json字符串,diff 492 yapi_res_body = models.TextField(blank=True, null=True, verbose_name='yapi定义-响应body') # json字符串,diff 493 # 参数模板 494 req_headers = models.TextField(blank=True, null=True, verbose_name='请求头') # json字符串 495 req_params = models.TextField(blank=True, null=True, verbose_name='请求参数-params') # json字符串 496 req_data = models.TextField(blank=True, null=True, verbose_name='请求参数-data') # json字符串 497 req_json = models.TextField(blank=True, null=True, verbose_name='请求参数-json') # json字符串 498 validator = models.TextField(blank=True, null=True, verbose_name='响应数据验证') # json字符串 499 500 update_status = models.IntegerField(default=1, verbose_name="更新状态", choices=API_UPDATE_STATUS_CHOICE) 501 labels = models.ManyToManyField(GlobalLabel, blank=True, default=[], 502 verbose_name='标签', related_name="api_label") 503 creator = models.ForeignKey(User, on_delete=models.SET_NULL, null=True, max_length=20, 504 verbose_name="创建人", related_name="api_info_creator") 505 updater = models.ForeignKey(User, on_delete=models.SET_NULL, null=True, max_length=20, 506 verbose_name="更新人", related_name="api_info_updater") 507 508 def __unicode__(self): 509 return self.name 510 511 def __str__(self): 512 return self.name 513 514 class Meta: 515 verbose_name = '接口' 516 verbose_name_plural = '接口管理' 517 518 519 # 接口变更历史 520 class ApiUpdateHistory(models.Model): 521 id = models.AutoField(primary_key=True) 522 api = models.ForeignKey(ApiInfo, related_name='update_api', on_delete=models.CASCADE, verbose_name='所属接口') 523 event = models.CharField(blank=True, null=True, max_length=100, 524 verbose_name='变更事件') # "interface_add", "interface_del", "interface_update" 525 content = models.TextField(blank=True, null=True, verbose_name='变更内容') 526 updater = models.CharField(blank=True, null=True, max_length=50, verbose_name="更新人") 527 update_time = models.DateTimeField(verbose_name='变更时间', auto_now=True) 528 update_status = models.IntegerField(default=0, verbose_name="变更处理状态", choices=API_UPDATE_STATUS_CHOICE) 529 530 def __unicode__(self): 531 return self.id 532 533 def __str__(self): 534 return self.id 535 536 class Meta: 537 verbose_name = '接口变更历史' 538 verbose_name_plural = '接口变更历史管理' 539 540 541 # yapi接口变更事件 -- 即事件待同步处理任务列表,yapi hook秒call几十上百次变更,同步处理完成不了,存储事件表后遍历处理 542 class YApiEvent(models.Model): 543 id = models.AutoField(primary_key=True) 544 yapi_id = models.IntegerField(default=0, verbose_name='YAPI接口ID') 545 event = models.CharField(blank=True, null=True, max_length=100, verbose_name='变更事件') 546 content = models.TextField(blank=True, null=True, verbose_name='变更内容') 547 updater = models.CharField(blank=True, null=True, max_length=50, verbose_name="更新人") 548 update_time = models.DateTimeField(verbose_name='变更时间', auto_now=True) 549 550 def __unicode__(self): 551 return self.id 552 553 def __str__(self): 554 return self.id 555 556 class Meta: 557 verbose_name = 'yapi接口事件' 558 verbose_name_plural = 'yapi接口事件管理' 559 560 561 # 测试用例集 562 class TestSuite(BaseModel): 563 """测试用例集""" 564 from django.core import serializers 565 serializers.get_serializer("json")() 566 567 id = models.AutoField(primary_key=True) 568 name = models.CharField(max_length=50, verbose_name='用例集') 569 safe_name = models.SlugField(max_length=50, verbose_name='用例集标识(用作py文件夹名)') 570 department = models.ForeignKey(Department, related_name='suite_dept', on_delete=models.CASCADE, null=True, verbose_name='部门') 571 headers = models.TextField(blank=True, null=True, verbose_name='请求头') 572 labels = models.ManyToManyField(GlobalLabel, blank=True, default=[], verbose_name='标签', related_name="suite_label") 573 574 setup = models.JSONField(blank=True, null=True, default=list, verbose_name='setup') 575 setup_class = models.JSONField(blank=True, null=True, default=list, verbose_name='setup_class') 576 teardown = models.JSONField(blank=True, null=True, default=list, verbose_name='teardown') 577 teardown_class = models.JSONField(blank=True, null=True, default=list, verbose_name='teardown_class') 578 579 creator = models.ForeignKey(User, on_delete=models.SET_NULL, null=True, max_length=20, 580 verbose_name="创建人", related_name="suite_creator") 581 updater = models.ForeignKey(User, on_delete=models.SET_NULL, null=True, max_length=20, 582 verbose_name="更新人", related_name="suite_updater") 583 584 def __unicode__(self): 585 return self.name 586 587 def __str__(self): 588 return self.name 589 590 class Meta: 591 verbose_name = '用例集' 592 verbose_name_plural = '用例集管理' 593 594 595 # 测试用例 596 class TestCase(BaseModel): 597 """测试用例""" 598 TEST_TYPE_CHOICE = ( 599 ('单接口测试', '单接口测试'), 600 ('场景测试', '场景测试'), 601 ('性能测试', '性能测试'), 602 ('setup', 'setup'), 603 ('teardown', 'teardown'), 604 ) 605 SEVERITY_CHOICE = ( 606 ('blocker', '阻塞缺陷(功能未实现,无法下一步)'), 607 ('critical', '严重缺陷(功能点缺失)'), 608 ('normal', '一般缺陷(边界情况,格式错误)'), 609 ('minor', '次要缺陷(界面错误与ui需求不符)'), 610 ('trivial', '轻微缺陷(必须项无提示,或者提示不规范)'), 611 ) 612 id = models.AutoField(primary_key=True) 613 name = models.CharField(max_length=50, verbose_name='用例名称') 614 safe_name = models.SlugField(max_length=50, verbose_name='用例集标识(用作py类名)') 615 test_suite = models.ForeignKey(TestSuite, related_name='case_suite', on_delete=models.CASCADE, verbose_name='所属用例集') 616 labels = models.ManyToManyField(GlobalLabel, blank=True, default=[], verbose_name='标签', related_name="case_label") 617 type = models.CharField(verbose_name='测试类型', choices=TEST_TYPE_CHOICE, default='单接口测试', max_length=20) 618 variables = models.TextField(blank=True, null=True, verbose_name='用例变量') # json字符串,Dict[Text, Any] 619 depends = models.TextField(blank=True, null=True, verbose_name='依赖项(用例)') # depends依赖项 620 severity = models.CharField(verbose_name='用例等级', choices=SEVERITY_CHOICE, default='normal', max_length=20) # 暂时没使用 621 result = models.CharField(verbose_name='测试结果', choices=RESULT_CHOICE, max_length=30, default='null') 622 creator = models.ForeignKey(User, on_delete=models.SET_NULL, null=True, max_length=20, 623 verbose_name="创建人", related_name="case_creator") 624 updater = models.ForeignKey(User, on_delete=models.SET_NULL, null=True, max_length=20, 625 verbose_name="更新人", related_name="case_updater") 626 627 def __unicode__(self): 628 return self.name 629 630 def __str__(self): 631 return self.name 632 633 class Meta: 634 verbose_name = '用例' 635 verbose_name_plural = '用例管理' 636 637 638 # 测试用例步骤 639 class TestStep(BaseModel): 640 """ 641 测试用例步骤 642 """ 643 id = models.AutoField(primary_key=True) 644 sid = models.IntegerField(default=0, verbose_name='执行步骤ID') 645 name = models.CharField(max_length=50, verbose_name='步骤名称') 646 test_case = models.ForeignKey(TestCase, related_name='step_case', on_delete=models.CASCADE, verbose_name='所属用例') 647 apiInfo = models.ForeignKey(ApiInfo, related_name='step_api', on_delete=models.CASCADE, verbose_name='所属接口') 648 labels = models.ManyToManyField(GlobalLabel, blank=True, default=[], verbose_name='标签', related_name="step_label") 649 depends = models.ManyToManyField('self', blank=True, default=[], verbose_name='依赖项(步骤)', related_name='step_depends') # depends依赖项 650 skipif = models.TextField(blank=True, null=True, default='', verbose_name='skipif') # 是否跳过步骤:条件表达式 651 setup_hooks = models.JSONField(blank=True, null=True, default=list, verbose_name='setup_hooks') 652 teardown_hooks = models.JSONField(blank=True, null=True, default=list, verbose_name='teardown_hooks') 653 req_path_extend = models.CharField(blank=True, null=True, max_length=1024, default='', verbose_name='请求path扩展') 654 req_headers = models.TextField(blank=True, null=True, verbose_name='请求头') # json字符串 655 req_params = models.TextField(blank=True, null=True, verbose_name='请求参数-params') # json字符串 656 req_json = models.TextField(blank=True, null=True, verbose_name='请求参数-json') # json字符串 657 req_data = models.TextField(blank=True, null=True, verbose_name='请求参数-data') # json字符串 658 validator = models.TextField(blank=True, null=True, verbose_name='响应数据验证') # json字符串 659 extractor = models.TextField(blank=True, null=True, verbose_name='响应数据变量提取') # json字符串 660 result = models.CharField(verbose_name='测试结果', choices=RESULT_CHOICE, max_length=30, default='null') 661 creator = models.ForeignKey(User, on_delete=models.SET_NULL, null=True, max_length=20, 662 verbose_name="创建人", related_name="step_creator") 663 updater = models.ForeignKey(User, on_delete=models.SET_NULL, null=True, max_length=20, 664 verbose_name="更新人", related_name="step_updater") 665 666 def __unicode__(self): 667 return self.description 668 669 def __str__(self): 670 return self.description 671 672 class Meta: 673 verbose_name = '测试用例步骤' 674 verbose_name_plural = '测试用例步骤管理' 675 676 677 # 测试报告 678 class TestReport(BaseModel): 679 BUILD_STATUS_CHOICE = ( 680 ('build-status-static', '构建完成'), 681 ('build-status-in-progress', '构建正在进行中') 682 ) 683 id = models.AutoField(primary_key=True) 684 # 构建信息 685 create_time = models.DateTimeField('创建时间', auto_now_add=True) 686 build_type = models.CharField(verbose_name='构建类型', choices=BUILD_TYPE_CHOICE, default='其他', max_length=20) 687 build_status = models.CharField(verbose_name='构建状态', choices=BUILD_STATUS_CHOICE, default='build-status-in-progress', max_length=50) 688 env = models.ForeignKey(GlobalEnv, blank=True, null=True, related_name='report_env', on_delete=models.CASCADE, 689 verbose_name='报告关联测试环境') 690 # 结果 691 status = models.BooleanField(default=True, verbose_name='状态') 692 duration = models.IntegerField(default=0, verbose_name='耗时(秒)') 693 # case统计 694 case_total = models.IntegerField(default=0, verbose_name='用例总数') 695 case_passed = models.IntegerField(default=0, verbose_name='用例成功数量') 696 case_failed = models.IntegerField(default=0, verbose_name='用例失败数量') 697 case_skipped = models.IntegerField(default=0, verbose_name='用例跳过数量') 698 case_error = models.IntegerField(default=0, verbose_name='用例故障数量') 699 case_pass_rate = models.FloatField(default=0, verbose_name='用例通过率') 700 # step 统计 701 step_total = models.IntegerField(default=0, verbose_name='步骤总数') 702 step_passed = models.IntegerField(default=0, verbose_name='步骤成功数量') 703 step_failed = models.IntegerField(default=0, verbose_name='步骤失败数量') 704 step_skipped = models.IntegerField(default=0, verbose_name='步骤跳过数量') 705 step_error = models.IntegerField(default=0, verbose_name='步骤故障数量') 706 step_pass_rate = models.FloatField(default=0, verbose_name='步骤通过率') 707 broken_apis = models.JSONField(default=dict, verbose_name='阻塞接口') 708 # 报告、日志地址记录 709 client = models.CharField(default='localhost', max_length=50, blank=True, null=True, verbose_name='测试机器Client端') 710 log_path = models.TextField(max_length=500, blank=True, null=True, verbose_name='日志文件地址') 711 html_report_path = models.TextField(max_length=500, blank=True, null=True, verbose_name='pytest-html报告地址') 712 allure_xml_path = models.TextField(max_length=500, blank=True, null=True, verbose_name='allure xml数据地址') 713 allure_url = models.URLField(max_length=500, blank=True, null=True, verbose_name='allure 报告地址') 714 jenkins_job_name = models.CharField(max_length=100, blank=True, null=True, verbose_name='jenkins job name') 715 jenkins_build_number = models.IntegerField(default=0, blank=True, null=True, verbose_name='jenkins build number') 716 717 def __unicode__(self): 718 return self.status 719 720 def __str__(self): 721 return self.status 722 723 class Meta: 724 verbose_name = '测试报告' 725 verbose_name_plural = '测试报告' 726 727 728 # 测试任务 729 class TestTask(models.Model): 730 """测试任务""" 731 TAST_STATUS_CHOICE = ( 732 (0, '等待中'), 733 (1, '运行中'), 734 (2, '已完成'), 735 (3, '暂停'), 736 (4, '无效'), 737 (5, '异常') 738 ) 739 TEST_LEVEL_CHOICE = ( 740 ('test_suite', 'test_suite'), # 查询 用例集 并执行 741 ('test_case', 'test_case'), # 查询 用例 并执行 742 ('test_step', 'test_step'), # 查询 用例步骤 并执行 743 ) 744 # 任务 - 定时 745 id = models.AutoField(primary_key=True) 746 name = models.CharField(default='', max_length=100, verbose_name='名称') 747 priority = models.IntegerField(default=0, verbose_name='优先级') 748 cron = models.CharField(default='', max_length=100, verbose_name='cron表达式') 749 status = models.SmallIntegerField(choices=TAST_STATUS_CHOICE, default=0, verbose_name='状态') 750 next_run = models.CharField(blank=True, null=True, default='', max_length=50, verbose_name='下一次执行时间') 751 duration = models.IntegerField(default=0, verbose_name='耗时(秒)') 752 create_time = models.DateTimeField(auto_now_add=True, verbose_name='创建时间') 753 creator = models.ForeignKey(User, on_delete=models.SET_NULL, null=True, max_length=20, 754 verbose_name="创建人", related_name="task_creator") 755 # APScheduler job state 756 job_state = models.JSONField(blank=True, null=True, default=dict, verbose_name='job state') 757 758 # 测试参数 759 test_env = models.ForeignKey(GlobalEnv, blank=True, null=True, related_name='test_env', on_delete=models.CASCADE, verbose_name='执行环境') 760 test_validate = models.ForeignKey(GlobalResponseValidate, blank=True, null=True, related_name='test_validate', on_delete=models.CASCADE, verbose_name='校验规则') 761 test_level = models.CharField(blank=True, null=True, choices=TEST_LEVEL_CHOICE, default='test_case', max_length=20, 762 verbose_name='测试类别') 763 test_filters = models.JSONField(blank=True, null=True, default=dict, verbose_name='查询筛选器') # dict 764 765 def __unicode__(self): 766 return self.name 767 768 def __str__(self): 769 return self.name 770 771 class Meta: 772 verbose_name = '测试任务' 773 verbose_name_plural = '测试任务' 774 775 776 def default_cron_job(): 777 job_info = { 778 "id": 0, 779 "job_state": {}, 780 "next_run_time": '' 781 } 782 return job_info 783 784 785 # 测试环境: 验证、状态监控 786 class TestEnvMonitor(BaseModel): 787 """测试环境监控: 环境验证、冒烟测试、业务巡检""" 788 id = models.AutoField(primary_key=True) 789 env = models.ForeignKey(GlobalEnv, related_name='monitor_env', on_delete=models.CASCADE, verbose_name='被监控环境') 790 validate = models.ForeignKey(GlobalResponseValidate, related_name='monitor_validate', on_delete=models.CASCADE, verbose_name='校验规则') 791 cron = models.CharField(default='', blank=True, null=True, max_length=100, verbose_name='cron表达式') 792 cron_job = models.JSONField(default=default_cron_job, verbose_name='cron_job信息') 793 build_type = models.CharField(max_length=20, choices=BUILD_TYPE_CHOICE, default='其他', verbose_name='构建类型') 794 report = models.ForeignKey(TestReport, blank=True, null=True, related_name='env_report', on_delete=models.CASCADE, verbose_name='环境报告') 795 creator = models.ForeignKey(User, on_delete=models.SET_NULL, max_length=20, null=True, verbose_name="创建人", related_name="monitor_creator") 796 797 def __unicode__(self): 798 return self.env.name 799 800 def __str__(self): 801 return self.env.name 802 803 class Meta: 804 verbose_name = '测试环境监控' 805 verbose_name_plural = '测试环境监控'
-------- THE END --------