首页 > 其他分享 >使用unittest和Django搭配写一个接口测试平台

使用unittest和Django搭配写一个接口测试平台

时间:2022-12-25 10:31:57浏览次数:35  
标签:case obj form unittest request 接口 Django 用例 pk

一、项目需求:

每个测试项目下面有多个测试用例

1. 对测试项目的

. 增

. 删

. 改

. 查,查看该测试项目下面所有的测试用例

. 为该测试项目批量导入,添加测试用例

2. 对项目下的接口进行

. 增

. 删

. 改

. 查

. 单个用例的执行

. 批量执行选中的用例,并且将执行结果(html报告)下载到本地

3. 数据可视化

. 接口项目相关数据进行统计

. 用例执行情况进行统计

4. 定时任务

. 每个测试项目都有周期,在周期结束后,自动的将该项目中的所有用例,执行一遍,生成测试报告。

. 使用Django发邮件功能,将报告发送

5、相关功能截图

①整体流程

使用unittest和Django搭配写一个接口测试平台_django

②接口项目列表

 

使用unittest和Django搭配写一个接口测试平台_django_02

③为指定的接口测试项目批量导入

 

使用unittest和Django搭配写一个接口测试平台_用例_03

④为指定的接口测试项目添加用例

 

使用unittest和Django搭配写一个接口测试平台_django_04

⑤某个接口测试项目下的用例列表

 

使用unittest和Django搭配写一个接口测试平台_用例_05

二、项目框架搭建:

1. 创建框架起始目录结构

 

使用unittest和Django搭配写一个接口测试平台_html_06

2.settings里面配置static

STATICFILES_DIRS = [os.path.join(BASE_DIR, 'static')]

 

使用unittest和Django搭配写一个接口测试平台_html_07

3.创建static文件夹并引入AdminLTE-master前端框架、bootstrap、echarts、jquery

 

使用unittest和Django搭配写一个接口测试平台_django_08

4. base模板继承static里面的css、jpg、png、修改href、注释没用页面、自定义container

①修改前的base.html

使用unittest和Django搭配写一个接口测试平台_用例_09

②要修改的base.html位置

使用unittest和Django搭配写一个接口测试平台_html_10

 

5. index模板继承base模板重写content-header块、content块

 

使用unittest和Django搭配写一个接口测试平台_用例_11

6. 配置urls

from app01 import views

urlpatterns = [

    url(r'^admin/', admin.site.urls),

    url(r'^index/', views.index, name='index'),

]

 

使用unittest和Django搭配写一个接口测试平台_html_12

7. 视图views

from django.shortcuts import render

# 查看数据:

from app01 import models

def index(request):

项目主页功能:

    if request.method == "POST":

        pass

    else:

        it_obj = models.Interface.objects.all()

        return render(request, "index.html", {"it_obj": it_obj})

 

使用unittest和Django搭配写一个接口测试平台_django_13

8. 先跑下看看页面

 

使用unittest和Django搭配写一个接口测试平台_用例_14

三、项目具体实现:

1. 表结构设计

用例表和接口项目表是多对一的关系。

日志表和接口项目表是多对一的关系。

from django.db import models

class Interface(models.Model):

接口项目表:

接口项目名称')  # 长整型

接口项目描述')  # 文本类型

项目开始时间')    # DateField是日期、DateTimeField是日期时间

项目开始时间')

    def __str__(self):

返回重定向字符串:

        return self.project_title

    def zhangyu(self):

当项目下有用例的时候:

        if self.case_set.count():

            return "{}%".format(self.case_set.filter(case_pass_status=1).count() / self.case_set.count() * 100)

        else:

            return 0

    class Meta:

        ordering = ['project_start_time']

class Case(models.Model):

用例表:

所属接口')  # ForeignKey外键关联接口项目表

用例名称')

用例描述')

请求类型')  # 该字段也可以设置为choices字段,让前端下拉选择

请求URL')

用例的参数', default='')  # 在前段输入完整的json串

预期值')

    case_execute_status = models.IntegerField(choices=(

已执行'),

未执行'),

整型

    case_pass_status = models.IntegerField(choices=(

已通过'),

未通过'),

    ), default=2)

用例执行报告', default='')

用例执行时间', auto_now_add=True)  # auto_now_add前端控制显示

    def __str__(self):

        return self.case_title

class Log(models.Model):

日志表:

日志所属的接口项目')

测试执行报告')

创建时间', auto_now_add=True)

    class Meta:

        ordering = ['-log_create_time']

 

使用unittest和Django搭配写一个接口测试平台_django_15

①接口项目表

接口项目名称

接口项目描述

项目开始时间

项目结束时间

②用例表

用例所属的接口项目,外键

用例名称

用例描述

用例的请求类型

用例的请求url

用例的请求参数

预期值

执行状态:已执行和未执行

通过状态:未通过和已通过

用例的执行结果报告

执行时间

2. 数据库迁移

使用unittest和Django搭配写一个接口测试平台_django_16

①:makemigrations app01

 

使用unittest和Django搭配写一个接口测试平台_用例_17

②:migrate app01

 

使用unittest和Django搭配写一个接口测试平台_用例_18

3. index.html主页功能

# 导入日期:
import datetime
import json
# 导入处理文件模块:
import xlrd
from django.shortcuts import render, redirect, HttpResponse
# 查看数据:
from app01 import models
# 导入form:
from util import MyForm
# 导入django事务模块:
from django.db import transaction
# 导入JsonResponse:
from django.http import JsonResponse
# 导入用例处理功能:
from util import ExecuteCaseHandler
# 导入处理数据流响应:
from django.http import StreamingHttpResponse
from django.utils.encoding import escape_uri_path
# 导入FileResponse:
from django.http import FileResponse
# 导入处理表格功能:
from util import ShowTabHandler
def index(request):
# 项目主页功能:
# django创建日期:
# models.Interface.objects.create(
# project_title="项目3",
# project_desc="项目3的描述",
# project_start_time=datetime.datetime.date(datetime.datetime.now()),
# project_end_time=datetime.datetime.date(datetime.datetime.now()),
# )
if request.method == "POST":
pass
else:
it_obj = models.Interface.objects.all()
return render(request, "index.html", {"it_obj": it_obj})

使用unittest和Django搭配写一个接口测试平台_django_19

{#继承base模板:#}
{% extends 'base.html' %}
{#重写content-header块:#}
{% block content-header %}
<nav aria-label="breadcrumb">
<ol class="breadcrumb">
<li class="breadcrumb-item"><a href="#">Home</a></li>
<li class="breadcrumb-item active" aria-current="page">项目列表</li>
</ol>
</nav>
{% endblock %}
{#重写conten块:#}
{% block content %}
<div class="content">
<div class="container-fluid">
<div class="row">
{# class="col-lg-12"调框:#}
<div class="col-lg-12">
<div class="card">
<div class="card-header">
<h5 class="m-0">
<nav>
{# 链接到add_interface:#}
<a href="{% url 'add_interface' %}">创建项目</a>
</nav>
</h5>
</div>
<div class="card-body">
{# 渲染数据:#}
{% if it_obj %}
{# 建立表格:#}
<table class="table table-striped">
{# 建立表头:#}
<thead>
<tr>
<th>序号</th>
<th>项目名称</th>
<th>项目描述</th>
<th>用例数量</th>
<th>覆盖率</th>
<th>开始时间</th>
<th>结束时间</th>
<th>操作</th>
</tr>
</thead>
{# 建立表体:#}
<tbody>
{% for foo in it_obj %}
<tr>
<td>{{ forloop.counter }}</td>
<td>{{ foo.project_title }}</td>
<td>{{ foo.project_desc }}</td>
<td>{{ foo.case_set.count }}</td>
<!-- 计算公式:通过/用例总数 -->
<td>{{ foo.zhangyu }}</td>
<td>{{ foo.project_start_time | date:"Y-m-d" }}</td>
<td>{{ foo.project_end_time | date:"Y-m-d" }}</td>
<td>
{# 确定删除哪一个加foo.pk:#}
<a href="{% url 'del_interface' foo.pk %}" class="btn btn-danger btn-sm">删除项目</a>
<a href="{% url 'edit_interface' foo.pk %}" class="btn btn-default btn-sm">编辑项目</a>
<a href="{% url 'case_list' foo.pk %}" class="btn btn-success btn-sm">查看用例</a>
<a href="{% url 'add_case' foo.pk %}" class="btn btn-warning btn-sm">添加用例</a>
<a href="{% url 'import_excel' foo.pk %}" class="btn btn-dark btn-sm">批量导入</a>
</td>
</tr>
{% endfor %}
</tbody>
</table>
{% else %}
暂时还没有项目,去<a href="{% url 'add_interface' %}">创建项目</a>
{% endif %}
</div>
</div>
</div>
</div>
</div>
</div>
{% endblock %}

使用unittest和Django搭配写一个接口测试平台_用例_20

4. add_interface.html添加接口项目功能(使用form):

def add_interface(request):
# 添加接口项目功能:
if request.method == 'POST':
form_obj = MyForm.InterfaceModelForm(request.POST)
if form_obj.is_valid():
form_obj.save()
return redirect('/index/')
else:
return render(request, 'add_interface.html', {"form_obj": form_obj})
else:
# 使用InterfaceModelForm:
form_obj = MyForm.InterfaceModelForm()
return render(request, "add_interface.html", {"form_obj": form_obj})

使用unittest和Django搭配写一个接口测试平台_html_21

{% extends 'base.html' %}
{% block content-header %}
{% endblock %}
{% block content %}
<div class="content">
<div class="container-fluid">
<div class="row">
<div class="col-lg-12">
<div class="card">
<div class="card-header">
<h5 class="m-0">
<nav>
<a href="{% url 'index' %}">返回项目列表页</a>
</nav>
</h5>
</div>
<div class="card-body">
{# 使用MyForm里面的InterfaceModelForm、POST请求#}
<form action="" method="POST" novalidate>
{% csrf_token %}
{% for foo in form_obj %}
<div>
<label for="">{{ foo.label }}</label>
{{ foo }}
<span style="color:red;">{{ foo.errors.0 }}</span>
</div>
{% endfor %}
<div>
<input type="submit" value="提交" class="btn btn-success">
</div>
</form>
</div>
</div>
</div>
</div>
</div>
</div>
{% endblock %}

使用unittest和Django搭配写一个接口测试平台_用例_22

5. del_interface删除接口项目功能

def del_interface(request, pk):
# 删除项目接口功能 pk:项目记录的pk值:
models.Interface.objects.filter(pk=pk).delete()
return redirect('/index/')

使用unittest和Django搭配写一个接口测试平台_django_23

6. edit_interface编辑接口项目功能

def edit_interface(request, pk):
# 编辑项目接口功能 pk:项目记录的pk
obj = models.Interface.objects.filter(pk=pk).first()
if request.method == "POST":
form_obj = MyForm.InterfaceModelForm(request.POST, instance=obj)
if form_obj.is_valid():
form_obj.save()
return redirect('/index/')
else:
return render(request, 'edit_interface.html', {"form_obj": form_obj})
else:
form_obj = MyForm.InterfaceModelForm(instance=obj)
return render(request, 'edit_interface.html', {"form_obj": form_obj})

使用unittest和Django搭配写一个接口测试平台_用例_24

{% extends 'base.html' %}
{% block content-header %}
{% endblock %}
{% block content %}
<div class="content">
<div class="container-fluid">
<div class="row">
<div class="col-lg-12">
<div class="card">
<div class="card-header">
<h5 class="m-0">
<nav>
<a href="{% url 'index' %}">返回项目列表页</a>
</nav>
</h5>
</div>
<div class="card-body">
{# 使用MyForm里面的InterfaceModelForm、POST请求#}
<form action="" method="POST" novalidate>
{% csrf_token %}
{% for foo in form_obj %}
<div>
<label for="">{{ foo.label }}</label>
{{ foo }}
<span style="color:red;">{{ foo.errors.0 }}</span>
</div>
{% endfor %}
<div>
<input type="submit" value="提交" class="btn btn-success">
</div>
</form>
</div>
</div>
</div>
</div>
</div>
</div>
{% endblock %}

使用unittest和Django搭配写一个接口测试平台_用例_25

7. case_list用例列表展示功能

def case_list(request, pk):
# 展示项目下所有的用例列表,pk:项目记录的pk
if request.method == "POST":
pass
else:
obj = models.Case.objects.filter(case_sub_it__pk=pk)
it_obj = models.Interface.objects.filter(pk=pk).first()
return render(request, 'case_list.html', {"case_list_obj": obj, "it_obj": it_obj})

使用unittest和Django搭配写一个接口测试平台_html_26

{% extends 'base.html' %}
{#面包屑导航#}
{% block content-header %}
<nav aria-label="breadcrumb">
<ol class="breadcrumb">
<li class="breadcrumb-item"><a href="{% url 'index' %}">项目列表</a></li>
<li class="breadcrumb-item"><a href="{% url 'case_list' it_obj.pk %}">{{ it_obj.project_title }}</a></li>
<li class="breadcrumb-item active" aria-current="page">用例列表</li>
</ol>
</nav>
{% endblock %}
{% block content %}
<div class="content">
<div class="container-fluid">
<div class="row">
<div class="col-lg-12">
<div class="card">
<div class="card-header">
<h5 class="m-0">
<nav>
<a href="{% url 'index' %}">返回项目列表页</a>
</nav>
</h5>
</div>
<div class="card-body">
<form action="{% url 'execute_case' %}" method="post">
{% csrf_token %}
{% if case_list_obj %}
<table class="table table-striped">
<thead>
<tr>
<th>选择</th>
<th>序号</th>
<th>名称</th>
<th>描述</th>
<th>所属项目</th>
<th>URL</th>
<th>请求类型</th>
<th>期望值</th>
<th>执行状态</th>
<th>通过状态</th>
<th>报告</th>
<th>操作</th>
</tr>
</thead>
<tbody>
{% for foo in case_list_obj %}
<tr>
<td><input type="checkbox" value="{{ foo.pk }}" name="case_pk" class="p1"></td>
<td>{{ forloop.counter }}</td>
<td>{{ foo.case_title }}</td>
<td title="{{ foo.case_desc }}">{{ foo.case_desc | truncatechars:10 }}</td>
<td title="{{ foo.case_sub_it.project_title }}">{{ foo.case_sub_it.project_title }}</td>
<td title="{{ foo.case_url }}">{{ foo.case_url | truncatechars:20 }}</td>
<td>{{ foo.case_method }}</td>
<td>{{ foo.case_expect | truncatechars:20 }}</td>
<td>{{ foo.get_case_execute_status_display }}</td>
<td>{{ foo.get_case_pass_status_display }}</td>
<td>
{% if foo.case_report == '' %}

{% else %}
<a href="{% url 'download_case_report' foo.pk %}" download>下载</a>
{% endif %}
</td>
<td>
<a href="{% url 'del_case' foo.pk %}" class="btn btn-danger btn-sm">删除</a>
<a href="{% url 'edit_case' foo.pk %}" class="btn btn-default btn-sm">编辑</a>
<a href="{% url 'execute_case'%}" class="btn btn-warning btn-sm">执行</a>
</td>
</tr>
{% endfor %}
</tbody>
</table>
{% else %}
[{{ it_obj.project_title }}]下暂时还没有用例,去<a href="{% url 'add_case' it_obj.pk %}">创建</a>
{% endif %}
<input type="button" value="批量执行并下载报告" class="btn btn-success" id="sure">
<span id="errorMsg" style="color: red;"></span>
</form>
</div>
</div>
</div>
</div>
</div>
</div>
{% endblock %}
{% block js %}
<script src="https://cdn.bootcss.com/sweetalert/2.1.2/sweetalert.min.js"></script>
<script>
$("#sure").click(function () {
var arr = new Array();
$.each($(".p1"), function (index, item) {
if ($(item).get(0).checked) {
arr.push($(item).val())
}
});
if (arr.length == 0) {
// 说明用户未选中用例,需要给提示
// console.log(2222222, "未选中", arr);
$("#errorMsg").html("请勾选至少一个用例!")
} else {
swal({
title: "Successful",
text: "用例正在执行",
timer: 20000,
showConfirmButton: false
});
$.ajax({
url: "/execute_case/",
type: "POST",
data: {
"case_list": JSON.stringify(arr),
"csrfmiddlewaretoken": $("[name='csrfmiddlewaretoken']").val()
},
success: function (dataMsg) {
window.location.href = '/crontab_log/';
}
})
}
});
</script>
{% endblock %}

使用unittest和Django搭配写一个接口测试平台_用例_27

8. del_case删除用例功能

def del_case(request, pk):
# 删除用例记录,pk:用例的pk
# 首先从表中将case对象取出来,
case_obj = models.Case.objects.filter(pk=pk).first()
# 因为后续的返回需要,case对象所属项目的pk值,所以,我们先把该pk值拿到
interface_obj_pk = case_obj.case_sub_it.pk
# 然后在执行删除
case_obj.delete()
return redirect('/case_list/{}'.format(interface_obj_pk)) # 需要所属项目的pk值

使用unittest和Django搭配写一个接口测试平台_django_28

9. edit_case编辑用例功能

def edit_case(request, pk):
# 编辑用例,pk:用例的pk
case_obj = models.Case.objects.filter(pk=pk).first()
if request.method == "POST":
# 编辑用例返回之前恢复下状态:
case_obj.case_execute_status = 2
case_obj.case_pass_status = 2
case_obj.case_report = ""
form_obj = MyForm.CaseModelForm(request.POST, instance=case_obj)
if form_obj.is_valid():
form_obj.save()
return redirect('/case_list/{}'.format(case_obj.case_sub_it_id))
else:
return render(request, 'edit_case.html', {"form_obj": form_obj, "it_obj": case_obj})
else:
form_obj = MyForm.CaseModelForm(instance=case_obj)
return render(request, 'edit_case.html', {"form_obj": form_obj, "it_obj": case_obj})

使用unittest和Django搭配写一个接口测试平台_html_29

10. 面包屑导航功能

    <nav aria-label="breadcrumb">

        <ol class="breadcrumb">

项目列表</a></li>

            <li class="breadcrumb-item"><a href="{% url 'case_list' it_obj.pk %}">{{ it_obj.project_title }}</a></li>

用例列表</li>

        </ol>

</nav>

11. add_case添加用例功能

def add_case(request, pk):
# 为指定的项目添加一条记录 pk:接口项目的pk
it_obj = models.Interface.objects.filter(pk=pk).first()
if request.method == "POST":
form_obj = MyForm.CaseModelForm(request.POST)
if form_obj.is_valid():
form_obj.save()
return redirect('/case_list/{}'.format(pk))
else:
return render(request, 'add_case.html', {"form_obj": form_obj, "it_obj": it_obj})
else:
form_obj = MyForm.CaseModelForm()
return render(request, 'add_case.html', {"form_obj": form_obj, "it_obj": it_obj})

使用unittest和Django搭配写一个接口测试平台_django_30

12. import_excel用例批量导入功能

def import_excel(request, pk):
# 为指定的项目,批量导入用例,用例来自Excel表格,pk:项目记录的pk
obj = models.Interface.objects.filter(pk=pk).first()
if request.method == "POST":
try:
with transaction.atomic(): # 事物处理
res1 = request.FILES.get('it_file')
book = xlrd.open_workbook(file_contents=res1.read())
sheet = book.sheet_by_index(0)
for row in range(1, sheet.nrows):
row = sheet.row_values(row)
models.Case.objects.create(
case_sub_it_id=pk,
case_title=row[0],
case_desc=row[1],
case_url=row[2],
case_method=row[3],
case_params=row[4],
case_expect=row[5],
)
return redirect('/case_list/{}'.format(pk))
except Exception as e:
return render(request, 'import_excel.html', {"it_obj": obj, "error_msg": "上传的文件类型只能是 [xlsx] 或者 [xls] 类型的文件,报错详细:{}".format(e)})
else:
return render(request, 'import_excel.html', {"it_obj": obj})

使用unittest和Django搭配写一个接口测试平台_html_31

13. execute_case单个用例执行功能

def execute_case(request):
"""
执行单个用例 ,pk:用例的pk
1. 从前端获取所有记录的pk
2. 从数据库将该记录对象查询出来
3. 循环从对象中提取相关的参数,发requests请求,断言
4. 使用unittest生成测试报告
5. 更新数据库字段
6. 给前端一个反馈
"""
if request.method == "POST":
# 前端的数据是序列化后的,所以,后端先要反序列化:
case_list_pk = json.loads(request.POST.get('case_list'))
# 从数据库中匹配出来用例对象:
case_list = models.Case.objects.filter(pk__in=case_list_pk)
# 循环执行用例对象,断言
f = ExecuteCaseHandler.run_case(case_list)
return JsonResponse({"STATUS": "OK"})
else:
return JsonResponse({"code": 0, "message": "非法的请求方式"})

使用unittest和Django搭配写一个接口测试平台_django_32

14. 批量执行功能

15. 定时任务功能

def crontab_log(request):
# 批量执行和定时任务的日志列表页:
if request.method == 'POST':
return HttpResponse("定时任务页面")
else:
log_obj = models.Log.objects.all()
return render(request, 'crontab_log_list.html', {"log_obj": log_obj})

使用unittest和Django搭配写一个接口测试平台_django_33

16. django发邮件功能

17. 点击功能增加样式

使用unittest和Django搭配写一个接口测试平台_html_34

 

 

使用unittest和Django搭配写一个接口测试平台_django_35

18. 下载报告功能

使用unittest和Django搭配写一个接口测试平台_django_36

19. 临时文件优化BytesIO用法

20. 批量日志功能

21. 多线程开启定时任务功能

22. 可视化功能

①折线图

使用unittest和Django搭配写一个接口测试平台_用例_37

②饼图

使用unittest和Django搭配写一个接口测试平台_用例_38

 

 

使用unittest和Django搭配写一个接口测试平台_用例_39

 

 

23.用例编辑的bug处理

四、项目中遇到的问题:

1. orm中关于日期类型的字段前端不展示

不要手动在pycharm中自己去添加日期类型的记录,数据会转成时间戳类型的时间,在前端无法渲染。

2.用例编辑的bug处理



标签:case,obj,form,unittest,request,接口,Django,用例,pk
From: https://blog.51cto.com/u_15920572/5967946

相关文章

  • django中间件需要了解的方法 importlib模块 django操作cookie django操作session
    目录django中间件三个需要了解的方法process_viewprocess_exceptionprocess_template_response基于django中间件实现功能的插拔式设计importlib模块方式1:基于函数封装方式2......
  • django中只使用ModleForm的表单验证,而不使用ModleForm来渲染
    主题众所周知,django.forms极其强大,不少的框架也借鉴了这个模式,如Scrapy。在表单验证时,django.forms是一绝,也是面向对象的经典表现。但要用它来渲染表单那就不好玩了,除非写......
  • 接口自动化之request几种常见请求及响应方法
    request的几种常见方法1.request.get()发送get请求2.request.post()发送post请求3.request.delete()发送delete请求4.request.put()发送put请......
  • 【接口自动化测试实战】系统架构
    1、common:自己封装的一些工具模块excel操作的模块、日志模块2、conf:存放项目的配置文件3、library:用来存放,一些第三方的模块:ddt、HtmlTestRunnerNew4、logs:日志记......
  • Go 快速入门指南 - 接口和方法
    接口Go接口是隐式实现。 对于一个数据类型,无需声明它实现了哪些接口,只需要实现接口必需的方法即可。当然了,存在一个小问题就是:我们可能无意间实现了某个接口:),所以 ​......
  • Go 快速入门指南 - 判断是否实现接口
    判断是否实现接口Go里面没有 ​​implements​​ 关键字来判断一个结构体(对象)是否实现了某个接口, 要实现类似的功能,需要用到 ​​类型断言​​。类型断言//判断......
  • django中间件
    django中间件相当于django框架的保安,只要数据过来必须经过中间件的校验。django中间件默认有7个。可以在setings内MIDDLEWARE里查看看着是字符串其实底层就是导模块的......
  • django中间件了解的三个方法、基于Django的插拔式设计、cookie与session简介、基于Dja
    目录Django中间件三个了解的方法1.process_view2.process_exception3.process_template_response基于Django中间实现功能的插拔式设计模拟编写一个消息通知功能(微信、qq、......
  • 请求Https接口,第一次请求失败,第二次请求成功
    一、使用HttpWebRequest请求Https类型的接口,第一次请求失败,第二请求成功在第一次请求接口时,接口返回失败,提示:“接口参数解析为空,无法处理”;且接口返回的状态码为:200接着......
  • Java接口
    什么是接口?接口是一种约定,提供给需要实现功能类的一种方式,实现接口必须实现接口的抽象方法如何创建接口?通过interface关键字创建接口例如:publicinterface接口名{......