接口测试平台核心以Httprunner为接口用例运行框架,要将用例的数据持久化到数据库中,方便读取修改与存储,则需要按照 yaml/json 的数据结构来设计数据库。

数据库模型关系图的总体结构

请添加图片描述

执行命令 django-admin startproject AutoTpsite 创建一个项目名为 AutoTpsite 的Django项目

再执行python manage.py startapp sqtp命令创建一个名为 sqtp的应用

模型代码设计

其中,request,step和config参考HR对应部分的字段,由于其中会出现很多嵌套字段,所以1层的字段就用json数据类型来代替。

models.py下创建模型

配置config部分

class Config(models.Model):
    name = models.CharField('测试用例名称',max_length=128)
    base_url = models.CharField('IP/域名',max_length=256,null=True,blank=True) #可为空或者空白
    variables = models.JSONField('变量',null=True)
    parameters = models.JSONField('参数',null=True)
    export = models.JSONField('用例返回值',null=True)
    verify = models.BooleanField('https校验',default=False)

    def __str__(self):
        return self.name
        # 以 测试用例名称 对外展示

通过 models.JSONField 可指定此字段为存储类型为JSON格式。null=True 表示此字段可以为空,这个NULL指的是SQL NULL,如果想存储为JsonNULL,则可以使用 Value('null') 来实现

用例Case部分

class Case(models.Model):
    # 一对一关系指向配置config 关联约束选择  DO_NOTHING 不做删除,保留
    config = models.OneToOneField(Config,on_delete=models.DO_NOTHING)
    # 用例文件路径,用于后续数据的导出
    file_path = models.CharField('用例文件路径',max_length=1000,default='demo_case.json')

    def __str__(self):
        return self.config.name
        # 以 config名称(测试用例名称) 对外展示

测试用例与配置config的对应关系为 一对一 ,通过 models.OneToOneField 配置关联约束

测试步骤Step部分

class Step(models.Model):
    # related_name 反向查询名称 同个模型中,两个字段关联同1个模型,必须指定related_name ,且名字不能相同

    # 属于哪条测试用例,属于的用例被删除了,测试步骤也就没有存在的必要了 关联约束选择 CASCADE级联删除
    belong_case = models.ForeignKey(Case,on_delete=models.CASCADE,related_name='teststeps')
    # 引用的哪条测试用例,引用的用例被删除了,测试步骤可能还需要存在 关联约束选择  DO_NOTHING 不做删除,保留,或 SET_NULL 允许为null
    linked_case = models.ForeignKey(Case,on_delete=models.SET_NULL,null=True,related_name='linked_steps')

    name = models.CharField('测试步骤名称', max_length=128)
    variables = models.JSONField('变量', null=True)
    extract = models.JSONField('请求返回值', null=True)
    validate = models.JSONField('校验项', null=True)
    setup_hooks = models.JSONField('初始化', null=True)
    teardown_hooks = models.JSONField('清除', null=True)

    def __str__(self):
        return self.name
        # 以 测试步骤名称 对外展示

Httprunner测试框架中,测试步骤可以属于某条测试用例,一条测试用例中可能会存在多条测试用例;而测试步骤也能引用其他测试用例。

在数据存储的观点来看,测试步骤与测试用例之间的关系为 多对一 ,通过外键 ForeignKey 进行关联,但在模型的设计中,需要将这两种关联关系区分开来,所以需要有两个字段。两个字段关联同1个模型,这里需要通过related_name 反向查询名称进行区分,且名字不能相同

请求Request部分

class Request(models.Model):
    # 一对一关系指向测试步骤Step 关联约束选择 CASCADE级联删除
    step = models.OneToOneField(Step,on_delete=models.CASCADE,null=True)
    # method可选字段,二维元组
    method_choices = (
        (0, 'GET'),  # 参数1:保存在数据库中的值,参数2:对外显示的值
        (1, 'POST'),
        (2, 'PUT'),
        (3, 'DELETE'),
    )
    method = models.SmallIntegerField('请求方法',choices=method_choices,default=0)
    url = models.CharField('请求路径', default='/', max_length=1000)
    params = models.JSONField('url参数', null=True)
    headers = models.JSONField('请求头', null=True)
    cookies = models.JSONField('Cookies', null=True)
    data = models.JSONField('data参数', null=True)
    json = models.JSONField('json参数', null=True)
    
    def __str__(self):
        return self.url
        # 以 请求路径 对外展示

http请求方式通常有GET、POST、PUT、DELETE,像这种数据库存储字段的值有限定内容的时候,可以先创建一个二维元组,参数1表示保存在数据库中的值,参数2表示对外显示的值,通过choices指定创建的二维元素。数据库中只需要存0、1、2、3这些数字即可,不需要设定为CharField类型,设定为SmallIntegerField即可,这种方式可以减少数据存储的压力。

正向查询与反向查询解析

在1对1,1对多,多对多关系中都存在正向查询和反向查询,模型查询其关联的项目叫做正向查询

如多对一关系中,正向查询是多方查询单方,因为外键是定义在多方,通过模型对象的外键 即modelObj.foreigner 进行查询则为正向查询;反向查询是单方查找多方,通过模型对象的外键模型的小写_setmodelObj.foreigner_set 进行查询

在tests.py文件下进行测试

from django.test import TestCase

# Create your tests here.

from sqtp.models import Case,Config,Step,Request

class TestRelatedQuery(TestCase):

    def setUp(self) -> None:
       self.config1 = Config.objects.create(name='用例1',base_url='http://127.0.0.1:8080/')
       self.config2 = Config.objects.create(name='用例2',base_url='http://127.0.0.1:8888/')
       self.case1 = Case.objects.create(config=self.config1)
       self.case2 = Case.objects.create(config=self.config2)

    def test_steps_query(self):
       step1 = Step.objects.create(belong_case=self.case1, name='步骤1') # 步骤1关联用例1
       step1.linked_case = self.case2 # 步骤1引用用例1
       step1.save()
       step2 = Step.objects.create(belong_case=self.case2, name='步骤2') # 步骤2关联用例2

       # 正向查询
       print('===========正向查询===========')
       print(step1.belong_case) #查看step1所属用例
       print(step1.linked_case)  # 查看step1引用的用例
       print(step2.belong_case) #查看step2所属的用例

       # 反向查询
       print('===========反向查询===========')
       # related_name代替 step_set
       print(self.case1.teststeps.all()) # 查询用例1下面有哪些步骤
       print(self.case2.linked_steps.all()) # 查询用例2被哪些步骤引用

输出结果

===========正向查询===========
用例1
用例2
用例2
===========反向查询===========
<QuerySet [<Step: 步骤1>]>
<QuerySet [<Step: 步骤1>]>

外键在 Step模型 中定义,与 Case模型 进行关联,而 Case模型 与 Config模型 一对一关联。

通过Step查询关联的用例,则是正向查询

通过用例查询步骤,则是属于反向查询,正常情况下要通过模型小写_set(即step_set)进行查询,但是在Step模型 中比较特殊,两个字段关联同1个模型,所以需要用 related_name 代替 step_set

Json字段操作解析

模型中大部分字段都是json类型(Django模型 JSONField类型)存储,以Request(请求信息)模型为例,操作增删改查

在tests.py文件下进行测试

新增
from django.test import TestCase

# Create your tests here.

from sqtp.models import Case, Config, Step, Request
from django.db.models import Value

class TestJsonField(TestCase):

    def setUp(self) -> None:
        print('====================新增数据======================')
        req1 = Request.objects.create(method=1,url='/mgr/student/',data={"name":"小明","age":16,"address":"广东广州","school":{"PrimarySchool":"实验小学","SecondarySchool":"第一中学"}})
        req2 = Request.objects.create(method=0,url='/api/teacher/',data={"name":"小王老师","courses":"英语","address":"广东深圳"})
        req3 = Request.objects.create(method=3,url='/api/delete/',data={"id":105,"display_idx":1})
        req = Request.objects.all()
        print(req)

执行命令python manage.py test sqtp.tests.TestJsonField进行测试

通过objects.create()新增数据,通过Request.objects.all()查询数据,输出结果

====================新增数据======================
<QuerySet [<Request: /mgr/student/>, <Request: /api/teacher/>, <Request: /api/delete/>]>
查询
Json条件查询
class TestJsonField(TestCase):

    def setUp(self) -> None:
        print('====================新增数据======================')
        req1 = Request.objects.create(method=1,url='/mgr/student/',data={"name":"小明","age":16,"address":"广东广州","school":{"PrimarySchool":"实验小学","SecondarySchool":"第一中学"}})
        req2 = Request.objects.create(method=0,url='/api/teacher/',data={"name":"小王老师","courses":"英语","address":"广东深圳"})
        req3 = Request.objects.create(method=3,url='/api/delete/',data={"id":105,"display_idx":1})
        req = Request.objects.all()
        print(req)


    def test_json_01(self):
        print('====================查询数据======================')
        req1 = Request.objects.all().first()
        print(req1)
        print(req1.data)  # 查询data数据内容

        print('====================json条件查询======================')
        req2 = Request.objects.filter(data__age=16)
        print(req2)
        req3 = Request.objects.filter(data__school__PrimarySchool="实验小学")
        print(req3)
        

通过Request.objects.all().first()查询出第一条数据

若想要根据json里的内容筛选数据,可以根据字段名__参数名(注意是双下划线)查询json字段里的内容,存在多层嵌套,依旧可以查询成功

输出结果

====================查询数据======================
/mgr/student/
{'name': '小明', 'age': 16, 'address': '广东广州', 'school': {'PrimarySchool': '实验小学', 'SecondarySchool': '第一中学'}}
====================json条件查询======================
<QuerySet [<Request: /mgr/student/>]>
<QuerySet [<Request: /mgr/student/>]>
字段条件查询

字段查询是指如何指定 SQL WHERE子句 的内容。它们用作 QuerySet 的 filter()、exclude() 和 get() 方法的关键字参数。 其基本格式是:field__lookuptype=value(注意其中是双下划线),默认查找类型为exact(精确匹配)。

Django的数据库API支持20多种查询类型:

字段名 说明
exact 精确匹配
iexact 不区分大小写的精确匹配
contains 包含匹配
icontains 不区分大小写的包含匹配
in 在…之内的匹配
gt 大于
gte 大于等于
lt 小于
lte 小于等于
startswith 从开头匹配
istartswith 不区分大小写从开头匹配
endswith 不区分大小写从结尾处匹配
range 范围匹配
date 日期匹配
year 年份
iso_year 以ISO 8601标准确定的年份
month 月份
day 日期
week 第几周
week_day 周几
iso_week_day 以ISO 8601标准确定的星期几
quarter 季度
time 时间
hour 小时
minute 分钟
second
regex 区分大小写的正则匹配
iregex 不区分大小写的正则匹配

以上字段条件查询对非JSONField 类型的模型字段,同样适用

class TestJsonField(TestCase):

    def setUp(self) -> None:
        print('====================新增数据======================')
        req1 = Request.objects.create(method=1,url='/mgr/student/',data={"name":"小明","age":16,"address":"广东广州","school":{"PrimarySchool":"实验小学","SecondarySchool":"第一中学"}})
        req2 = Request.objects.create(method=0,url='/api/teacher/',data={"name":"小王老师","courses":"英语","address":"广东深圳"})
        req3 = Request.objects.create(method=3,url='/api/delete/',data={"id":105,"display_idx":1})


    def test_json_02(self):
        print('====================字段条件查询======================')
        req4 = Request.objects.filter(url__contains='mgr')
        print(req4)
        req5 = Request.objects.filter(method__in=[0,1,2])
        print(req5)

输出结果

====================字段条件查询======================
<QuerySet [<Request: /mgr/student/>]>
<QuerySet [<Request: /mgr/student/>, <Request: /api/teacher/>]>
跨关系查询

Django提供了强大并且直观的方式解决跨越关联的查询,它在后台自动执行包含 JOIN 的 SQL语句。要跨越某个关联,只需使用关联的模型字段名称,并使用双下划线分隔,直至你想要的字段(可以链式跨越,无限跨度)

class TestOverRelations(TestCase):
    def setUp(self) -> None:
        # 创建用例
        self.config1 = Config.objects.create(name='用例1', base_url='http://127.0.0.1:8080/')
        self.config2 = Config.objects.create(name='用例2', base_url='http://127.0.0.1:8888/')
        self.case1 = Case.objects.create(config=self.config1)
        self.case2 = Case.objects.create(config=self.config2)

    def test_step_request(self):
        # 准备测试数据 步骤和请求
        step1 = Step.objects.create(belong_case=self.case1,name='步骤1')
        step2 = Step.objects.create(belong_case=self.case1,name='步骤2')
        step3 = Step.objects.create(belong_case=self.case2,name='步骤3')
        req1 = Request.objects.create(method=0,url='/api/teacher1/',data={"name":"小王老师","courses":"英语","address":"广东深圳"},step=step1)
        req2 = Request.objects.create(method=1,url='/api/teacher2/',data={"name":"小王老师","courses":"英语","address":"广东深圳"},step=step2)
        req3 = Request.objects.create(method=2,url='/api/teacher3/',data={"name":"小王老师","courses":"英语","address":"广东深圳"},step=step3)

        print(req1.step.belong_case) # 链式语法 通过请求查询步骤再查询所属的用例(通过模型关联一步步查询    
        print(Request.objects.filter(step=step1))   
        # 跨关系查询
        print(Request.objects.filter(step__belong_case__config__name='用例1'))   # 查询 用例1 下的步骤

跨关系查询语法:字段__关联字段

跨关系查询是根据上级数据特征查找下级的结果

用例1
<QuerySet [<Request: /api/teacher1/>]>
<QuerySet [<Request: /api/teacher1/>, <Request: /api/teacher2/>]>
修改
from django.test import TestCase

# Create your tests here.

from sqtp.models import Case, Config, Step, Request
from django.db.models import Value

class TestJsonField(TestCase):

    def setUp(self) -> None:
        print('====================新增数据======================')
        req = Request.objects.create(method=1,url='/mgr/student/',data={"name":"小明","age":16,"address":"广东广州","school":{"PrimarySchool":"实验小学","SecondarySchool":"第一中学"}})
        
    def test_json_04(self):

        print('====================查询数据======================')
        req1 = Request.objects.all().first()
        print(req1)
        print(req1.data) # 查询data数据内容

        print('====================修改整个字段内容======================')
        req1.data={"name":"小王","age":16,"籍贯":"江西南昌"}
        req1.save()
        print(Request.objects.all().first().data) # 查看修改后的内容

        print('====================修改字段中json局部内容======================')
        req2 = Request.objects.all().first()
        print(req2.data['name']) # 修改前
        req2.data['name'] = '小红'
        req2.save()
        print(Request.objects.all().first().data['name']) # 修改后    
    

输出结果

====================查询数据======================
/mgr/student/
{'name': '小明', 'age': 16, 'address': '广东广州', 'school': {'PrimarySchool': '实验小学', 'SecondarySchool': '第一中学'}}
====================修改整个字段内容======================
{'name': '小王', 'age': 16, '籍贯': '江西南昌'}
====================修改字段中json局部内容======================
小王
小红

修改data字段的内容,data字段就变为了{'name': '小王', 'age': 16, '籍贯': '江西南昌'};如果只需要修改json数据中的部分内容,只需要对字典中下标名称进行赋值即可,保存后json数据的内容就修改成功了

删除
from django.test import TestCase

# Create your tests here.

from sqtp.models import Case, Config, Step, Request
from django.db.models import Value

class TestJsonField(TestCase):

    def setUp(self) -> None:
        print('====================新增数据======================')
        req = Request.objects.create(method=1,url='/mgr/student/',data={"name":"小明","age":16,"address":"广东广州","school":{"PrimarySchool":"实验小学","SecondarySchool":"第一中学"}})
        
    def test_json_05(self):
        print('====================查询数据======================')
        req1 = Request.objects.all().first()
        print(req1)
        print(req1.data) # 查询data数据内容

        print('====================删除字段局部的内容======================')
        req1.data.pop('name')
        req1.save()
        print(Request.objects.all().first().data)

        print('====================删除整个字段的内容======================')
        req1.data = Value('null') # 设置成json的null
        # req1.data = None # 设置成sql的null
        req1.save()
        print(Request.objects.all().first().data)

输出结果

====================查询数据======================
/mgr/student/
{'name': '小明', 'age': 16, 'address': '广东广州', 'school': {'PrimarySchool': '实验小学', 'SecondarySchool': '第一中学'}}
====================删除字段局部的内容======================
{'age': 16, 'address': '广东广州', 'school': {'PrimarySchool': '实验小学', 'SecondarySchool': '第一中学'}}
====================删除整个字段的内容======================
None

删除字段json数据中的内容,则是通过pop()函数完成;删除整个字段的数据,则是字段内容置为null

Logo

GitCode 天启AI是一款由 GitCode 团队打造的智能助手,基于先进的LLM(大语言模型)与多智能体 Agent 技术构建,致力于为用户提供高效、智能、多模态的创作与开发支持。它不仅支持自然语言对话,还具备处理文件、生成 PPT、撰写分析报告、开发 Web 应用等多项能力,真正做到“一句话,让 Al帮你完成复杂任务”。

更多推荐