Commit 2e2f0b00 authored by wangmeng's avatar wangmeng

Default Changelist

parents
# Created by .ignore support plugin (hsz.mobi)
### Python template
# Byte-compiled / optimized / DLL files
__pycache__/
*.py[cod]
*$py.class
# C extensions
*.so
# Distribution / packaging
.Python
build/
develop-eggs/
dist/
downloads/
eggs/
.eggs/
lib/
lib64/
parts/
sdist/
var/
wheels/
share/python-wheels/
*.egg-info/
.installed.cfg
*.egg
MANIFEST
# PyInstaller
# Usually these files are written by a python script from a template
# before PyInstaller builds the exe, so as to inject date/other infos into it.
*.manifest
*.spec
# Installer logs
pip-log.txt
pip-delete-this-directory.txt
# Unit test / coverage reports
htmlcov/
.tox/
.nox/
.coverage
.coverage.*
.cache
nosetests.xml
coverage.xml
*.cover
*.py,cover
.hypothesis/
.pytest_cache/
cover/
# Translations
*.mo
*.pot
# Django stuff:
*.log
local_settings.py
db.sqlite3
db.sqlite3-journal
# Flask stuff:
instance/
.webassets-cache
# Scrapy stuff:
.scrapy
# Sphinx documentation
docs/_build/
# PyBuilder
.pybuilder/
target/
# Jupyter Notebook
.ipynb_checkpoints
# IPython
profile_default/
ipython_config.py
# pyenv
# For a library or package, you might want to ignore these files since the code is
# intended to run in multiple environments; otherwise, check them in:
# .python-version
# pipenv
# According to pypa/pipenv#598, it is recommended to include Pipfile.lock in version control.
# However, in case of collaboration, if having platform-specific dependencies or dependencies
# having no cross-platform support, pipenv may install dependencies that don't work, or not
# install all needed dependencies.
#Pipfile.lock
# PEP 582; used by e.g. github.com/David-OConnor/pyflow
__pypackages__/
# Celery stuff
celerybeat-schedule
celerybeat.pid
# SageMath parsed files
*.sage.py
# Environments
.env
.venv
env/
venv/
ENV/
env.bak/
venv.bak/
# Spyder project settings
.spyderproject
.spyproject
# Rope project settings
.ropeproject
# mkdocs documentation
/site
# mypy
.mypy_cache/
.dmypy.json
dmypy.json
# Pyre type checker
.pyre/
# pytype static type analyzer
.pytype/
# Cython debug symbols
cython_debug/
log/
.idea
## 执行命令
>celery的运行:celery -A celery_tasks.main worker -l info
> 超级管理员:python3 manage.py createsuperuser
>本地启动服务:python3 manage.py runserver --settings=fusionTest.setting.local
>dev启动服务:python3 manage.py runserver --settings=fusionTest.setting.dev
>staging启动服务:python3 manage.py runserver --settings=fusionTest.setting.staging
## mixins试图工具集
>RetrieveModelMixin:retrieve 单取
>ListModelMixin:list 群取
>CreateModelMixin:create 单增
>UpdateModelMixin:update 单整体改
>UpdateModelMixin: partial_update 单局部改
>DestroyModelMixin:destroy 单删
##admin站点
>登录使用username,密码未加密
##平台登录
>登录使用mobile,密码加密方式aes+base64
#认证
>采取jwt认证,登录后返回token,在请求头传{"Authorization":"jwt {token}"}
#异步任务
>1发送验证码
>2发送邮箱
#选型
>语言:python
>后端框架:django rest framework
>单元测试框架:unittest
>前端框架:vue
>数据库:mysql,redis
>任务调度:celery、scheduler
>认证:jwt
>报告:BeautifulReport
#部署
1. 从官方拉取python基础镜像到服务器的镜像仓库
2. 生成django镜像
3. 生成容器
# -*-coding:utf-8 -*-
# __author__ = 'wuhongbin'
# Time:2021/1/23 4:35 下午
default_app_config = 'apps.apiTest.apps.ApitestConfig' #
from django.contrib import admin
from .models import *
admin.site.site_header = "丽维家测试后台"
admin.site.site_title = "丽维家测试后台"
@admin.register(Project)
class ProjectAdmin(admin.ModelAdmin):
"""项目表"""
list_display = ['name','type','description']
@admin.register(Host)
class ProjectAdmin(admin.ModelAdmin):
"""host"""
list_display = ['name','host','description','project']
@admin.register(Api)
class ProjectAdmin(admin.ModelAdmin):
"""api接口"""
list_display = ['name','http_method','host','project','path','headers','request_type','data','description','expect_code']
@admin.register(ApiArgument)
class ProjectAdmin(admin.ModelAdmin):
"""api的全局参数"""
list_display = ['api','name','value']
@admin.register(ApiArgumentExtract)
class ProjectAdmin(admin.ModelAdmin):
"""用例API的响应参数提取"""
list_display = ['api','name','origin','format']
@admin.register(RunApiRecord)
class ProjectAdmin(admin.ModelAdmin):
"""API运行记录"""
list_display = ['url','http_method','create_time']
# -*-coding:utf-8 -*-
# __author__ = 'wuhongbin'
# Time:2021/2/14 1:29 上午
import requests
from urllib import parse
import json
import re
from requests.packages.urllib3.exceptions import InsecureRequestWarning
requests.packages.urllib3.disable_warnings(InsecureRequestWarning)
from lwjTest.settings import logger
def _replace_argument(target_str,arguments):
"""
:param target_str: 原始数据
:param arguments: 需要替换的数据
:return:
"""
# if type(target_str)==dict:
# target_str = json.dumps(target_str)
# if not arguments:
# return target_str
# while True:
# search_result = re.search(r"{{(.+?)}}",target_str)
# if not search_result:
# break
# argument_name = search_result.group(1)
# if argument_name in arguments:
# target_str = re.sub("{{"+argument_name+"}}",arguments[argument_name],target_str)
# else:
# target_str = re.sub("{{"+argument_name+"}}",argument_name,target_str)
# return target_str
if type(target_str)==str:
if not arguments:
return target_str
while True:
search_result = re.search(r"{{(.+?)}}",target_str)
if not search_result:
break
argument_name = search_result.group(1)
if argument_name in arguments:
target_str = re.sub("{{"+argument_name+"}}",arguments[argument_name],target_str)
else:
target_str = re.sub("{{"+argument_name+"}}",argument_name,target_str)
return target_str
elif type(target_str)==dict:
target_str = json.dumps(target_str)
if not arguments:
return target_str
while True:
search_result = re.search(r"{{(.+?)}}",target_str)
if not search_result:
break
argument_name = search_result.group(1)
if argument_name in arguments:
target_str = re.sub("{{"+argument_name+"}}",arguments[argument_name],target_str)
else:
target_str = re.sub("{{"+argument_name+"}}",argument_name,target_str)
return json.loads(target_str)
elif type(target_str)==list:
return target_str
elif type(target_str)==int:
return target_str
def apiRequest(api,arguments=None,reset_data=None):
host = api.host.host
method = api.http_method
request_type = api.request_type
path = api.path
url = parse.urljoin(host, path)
url = _replace_argument(url,arguments)
logger.info("请求的url:{}".format(url))
#替换请求参数的变量
data = {}
#判断请求参数是否被重写
# if reset_data:
# #重写请求参数
# data_dict = json.loads(reset_data, encoding='utf-8')
# for key,value in data_dict.items():
# data[key] = _replace_argument(value,arguments)
# elif api.data:
# #使用以前的请求参数
# data_dict = json.loads(api.data, encoding='utf-8')
# for key,value in data_dict.items():
# data[key] = _replace_argument(value,arguments)
if reset_data:
#重写请求参数
data_list = json.loads(reset_data, encoding='utf-8')
for data_dict in data_list:
for key,value in data_dict.items():
data[key] = _replace_argument(value,arguments)
elif api.data:
#使用以前的请求参数
data_list = json.loads(api.data, encoding='utf-8')
for data_dict in data_list:
for key,value in data_dict.items():
data[key] = _replace_argument(value,arguments)
headers = {}
if api.headers:
header_list = json.loads(api.headers, encoding='utf-8')
for header_dict in header_list:
for key,value in header_dict.items():
headers[key] = _replace_argument(value,arguments)
logger.info("headers:{}".format(headers))
logger.info("==============发起请求====================")
if request_type=="json":
res = requests.request(method, url, headers=headers, json=data,verify=False)
logger.info("==============请求结束====================")
logger.info("response:{}".format(res.text))
elif request_type=="form-data":
res = requests.request(method, url, headers=headers, data=data,verify=False)
logger.info("==============请求结束====================")
logger.info("response:{}".format(res.text))
return res
from django.apps import AppConfig
class ApitestConfig(AppConfig):
name = 'apps.apiTest'
verbose_name = "api"
# -*-coding:utf-8 -*-
# __author__ = 'wuhongbin'
# Time:2021/4/6 5:40 下午
# -*-coding:utf-8 -*-
# __author__ = 'wuhongbin'
# Time:2021/2/26 11:14 上午
from io import BytesIO
import xlwt
import datetime
from apps.chanDao.models import ChanDaoCase
from rest_framework.views import APIView
from django.http import HttpResponse
from django.db.models import Model
from django.core.paginator import Paginator
from django.db.models.query import QuerySet
class DataDumpView(APIView):
"""将数据库记录转换为excel下载下来"""
model = None # 模型类,要导出的表
order_field = '' # 排序依据字段,只允许一个
fields = '__all__' # 表头以及字段,例{'num': '单号', 'price': '金额', 'add': '生单时间'}
# __all__则代表所有,默认取verbose_name作为表头
method_fields = {} # 表中不存在的字段,需要自己实现对应的get__<字段名方法>
exclude = [] # 将这些字段从要导出的字段中排除
limit = 2000 # 当没有筛选条件时返回的记录条数
sheet_size = 300 # 每个sheet记录数(不含表头)
file_name = None # 生成的xls文件名
permission_classes = [] # 权限,此处要求子类必须覆盖,否则永远没有权限
_protect_functions = ['get', ]
def _get_paginator(self, queryset):
"""获取分页对象"""
assert isinstance(queryset, QuerySet), (
'queryset 必须是 Queryset类型,但是获取到一个{}'.format(type(queryset))
)
try:
return Paginator(queryset.order_by(self.order_field), self.sheet_size)
except:
return Paginator(queryset, self.sheet_size)
def get_queryset(self, request):
"""
默认返回2000条数据
如果需要筛选,则重写此方法
"""
return self.model.objects.all().order_by(self.order_field)[:self.limit]
def _get_method_fields(self, obj):
"""通过指定function获取表中不存在的值
这个字段位于method_fields列表里面
"""
for field in self.method_fields:
func_name = 'get__{}'.format(field)
if not hasattr(self, func_name):
raise Exception(
'你在method_fields中定义了字段{},但是你并没有实现{}方法.'.format(
field,
func_name
)
)
setattr(obj, field, getattr(self, func_name)(obj))
return obj
def _get_relation_fields(self):
"""用于获取外键字段的属性"""
raise Exception('这个方法将在下个版本实现')
def get_format_value(self, obj, field):
"""将对象属性格式化"""
attr = getattr(obj, field)
value = attr
# 外键格式化
if isinstance(attr, Model):
value = attr.__str__()
# 时间类型格式化
if isinstance(attr, datetime.datetime):
value = attr.strftime('%Y-%m-%d %H:%M:%S')
# 其他的暂时不做转换
return value
def _get_fields_map(self):
"""根据fields, exclude生成最终的fields_map"""
fields_map = {}
if self.fields == '__all__':
# 获取所有字段
fields_map = {field.name: field.verbose_name for field in self.model._meta.fields}
elif isinstance(self.fields, dict):
# 获取指定字段
fields_map = self.fields
# 要排除的字段
del_keys = [field for field in self.fields if field in self.exclude]
for d_k in del_keys:
if d_k in fields_map:
del fields_map[d_k]
return fields_map
def _get_file_io(self, request):
"""在io中生成excel文件"""
x_io = BytesIO() # io
work_book = xlwt.Workbook() # 工作簿
style = xlwt.XFStyle()
font = xlwt.Font()
font.name = 'SimSun' # 指定“宋体”
style.font = font
queryset = self.get_queryset(request)
paginator = self._get_paginator(queryset)
for sheet in paginator.page_range:
work_sheet = work_book.add_sheet(sheetname='sheet{}'.format(sheet))
objs = paginator.page(sheet)
row = 0
for obj in objs:
# 写下每一行
col = 0
fields_map = self._get_fields_map()
# 获取obj通过method_fields获取的属性
o = self._get_method_fields(obj)
fields_map.update(self.method_fields)
for field in fields_map:
# 填充单元格
if row == 0:
work_sheet.write(row, col, fields_map.get(field), style)
work_sheet.write(row+1, col, self.get_format_value(o, field), style)
else:
work_sheet.write(row+1, col, self.get_format_value(o, field), style)
col += 1
row += 1
work_book.save(x_io)
return x_io
# for sheet in paginator.page_range:
# work_sheet = work_book.add_sheet(sheetname='sheet{}'.format(sheet))
# objs = paginator.page(sheet)
# row = 0
# for obj in objs:
# # 写下每一行
# col = 0
# fields_map = self._get_fields_map()
# # 获取obj通过method_fields获取的属性
# o = self._get_method_fields(obj)
# fields_map.update(self.method_fields)
# for field in fields_map:
# # 填充单元格
# if row == 0:
# work_sheet.write(row, col, fields_map.get(field), style)
# else:
# work_sheet.write(row, col, self.get_format_value(o, field), style)
# col += 1
# row += 1
# work_book.save(x_io)
# return x_io
def get_file_name(self):
"""获取生成文件的名字"""
if self.file_name:
return self.file_name
elif hasattr(self.model, 'Meta'):
if hasattr(self.model.Meta, 'verbose_name'):
return self.model.Meta.verbose_name
return self.model.__class__.__name__
def _get_response(self, file_io, file_name):
"""得到响应对象"""
res = HttpResponse()
res["Content-Type"] = "application/vnd.ms-excel"
res["Content-Disposition"] = 'attachment;filename={}.xls'.format(file_name).encode()
res.write(file_io.getvalue())
return res
def get(self, request):
"""主响应函数,接受GET请求"""
# 在内存中准备好文件io
file_io = self._get_file_io(request)
file_name = self.get_file_name()
# 确定响应
response = self._get_response(file_io, file_name)
return response
\ No newline at end of file
# Generated by Django 3.1.2 on 2021-02-10 01:42
from django.db import migrations, models
import django.db.models.deletion
class Migration(migrations.Migration):
initial = True
dependencies = [
]
operations = [
migrations.CreateModel(
name='Api',
fields=[
('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('name', models.CharField(max_length=50, verbose_name='接口名称')),
('http_method', models.CharField(choices=[('POST', 'POST'), ('GET', 'GET'), ('PUT', 'PUT'), ('DELETE', 'DELETE')], max_length=50, verbose_name='请求方式')),
('path', models.CharField(max_length=1024, verbose_name='接口地址')),
('headers', models.TextField(blank=True, null=True, verbose_name='请求头')),
('request_type', models.CharField(choices=[('json', 'json'), ('form-data', 'form-data')], default='form-data', max_length=20, verbose_name='请求类型')),
('data', models.TextField(blank=True, null=True, verbose_name='提交的数据')),
('description', models.CharField(blank=True, max_length=1024, null=True, verbose_name='描述')),
('expect_code', models.CharField(choices=[('200', '200'), ('201', '201'), ('202', '202'), ('203', '203'), ('204', '204'), ('301', '301'), ('302', '302'), ('400', '400'), ('401', '401'), ('403', '403'), ('404', '404'), ('405', '405'), ('406', '406'), ('407', '407'), ('408', '408'), ('500', '500'), ('502', '502')], default=200, max_length=10, verbose_name='期望返回的code')),
('expect_content', models.CharField(blank=True, max_length=200, null=True, verbose_name='期望返回的内容')),
('create_time', models.DateTimeField(auto_now=True, verbose_name='运行的时间')),
],
options={
'verbose_name': '接口信息',
'verbose_name_plural': '接口信息',
'db_table': 'fusion_api',
},
),
migrations.CreateModel(
name='Project',
fields=[
('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('name', models.CharField(max_length=50, verbose_name='项目名称')),
('type', models.CharField(choices=[['web', 'web'], ['app', 'app']], max_length=50, verbose_name='项目类型')),
('description', models.CharField(blank=True, max_length=1024, null=True, verbose_name='描述')),
('create_time', models.DateTimeField(auto_now=True, verbose_name='更新时间')),
('found_time', models.DateTimeField(auto_now_add=True, verbose_name='创建时间')),
],
options={
'verbose_name': '项目表',
'verbose_name_plural': '项目表',
'db_table': 'fusion_project',
},
),
migrations.CreateModel(
name='RunApiRecord',
fields=[
('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('url', models.CharField(max_length=200, verbose_name='请求的url')),
('http_method', models.CharField(choices=[('POST', 'POST'), ('GET', 'GET'), ('PUT', 'PUT'), ('DELETE', 'DELETE')], max_length=10, verbose_name='请求方式')),
('data', models.TextField(null=True, verbose_name='提交的数据')),
('headers', models.TextField(null=True, verbose_name='提交的header')),
('create_time', models.DateTimeField(auto_now=True, verbose_name='运行的时间')),
('return_code', models.CharField(max_length=10, verbose_name='返回的code')),
('return_content', models.TextField(null=True, verbose_name='返回的内容')),
('return_cookies', models.TextField(null=True, verbose_name='返回的cookies')),
('return_headers', models.TextField(null=True, verbose_name='返回的headers')),
('assert_result', models.CharField(max_length=10, null=True, verbose_name='断言结果')),
('api', models.ForeignKey(null=True, on_delete=django.db.models.deletion.CASCADE, to='apiTest.api', verbose_name='关联的API')),
],
options={
'verbose_name': 'api运行记录',
'verbose_name_plural': 'api运行记录',
'db_table': 'fusion_run_api_record',
'ordering': ['-create_time'],
},
),
migrations.CreateModel(
name='Host',
fields=[
('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('name', models.CharField(max_length=50, verbose_name='名称')),
('host', models.CharField(max_length=1024, verbose_name='Host地址')),
('description', models.CharField(blank=True, max_length=1024, null=True, verbose_name='描述')),
('create_time', models.DateTimeField(auto_now=True, verbose_name='更新时间')),
('found_time', models.DateTimeField(auto_now_add=True, verbose_name='创建时间')),
('project', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='host_list', to='apiTest.project', verbose_name='所属项目')),
],
options={
'verbose_name': 'host域名',
'verbose_name_plural': 'host域名',
'db_table': 'fusion_host',
},
),
migrations.CreateModel(
name='ApiArgumentExtract',
fields=[
('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('name', models.CharField(max_length=100, null=True, verbose_name='参数名字')),
('origin', models.CharField(choices=[('HEADER', 'HEADER'), ('BODY', 'BODY'), ('COOKIE', 'COOKIE')], max_length=20, null=True, verbose_name='参数来源')),
('format', models.CharField(max_length=100, null=True, verbose_name='参数获取的格式')),
('api', models.ForeignKey(null=True, on_delete=django.db.models.deletion.CASCADE, related_name='argumentExtract', to='apiTest.api', verbose_name='用例API')),
],
options={
'verbose_name': '用例API的参数提取',
'verbose_name_plural': '用例API的参数提取',
'db_table': 'fusion_api_argument_extract',
},
),
migrations.CreateModel(
name='ApiArgument',
fields=[
('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('name', models.CharField(max_length=100, null=True, verbose_name='参数名字')),
('value', models.CharField(max_length=100, null=True, verbose_name='参数的值')),
('api', models.ForeignKey(null=True, on_delete=django.db.models.deletion.CASCADE, related_name='arguments', to='apiTest.api', verbose_name='用例')),
],
options={
'verbose_name': 'api的参数',
'verbose_name_plural': 'api的参数',
'db_table': 'fusion_api_argument',
},
),
migrations.AddField(
model_name='api',
name='host',
field=models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='apiTest.host', verbose_name='host'),
),
migrations.AddField(
model_name='api',
name='project',
field=models.ForeignKey(null=True, on_delete=django.db.models.deletion.CASCADE, related_name='api_list', to='apiTest.project', verbose_name='项目'),
),
]
# Generated by Django 3.1.2 on 2021-02-19 05:54
from django.db import migrations, models
import django.db.models.deletion
class Migration(migrations.Migration):
dependencies = [
('apiTest', '0001_initial'),
]
operations = [
migrations.AlterField(
model_name='api',
name='host',
field=models.ForeignKey(null=True, on_delete=django.db.models.deletion.CASCADE, related_name='host_api', to='apiTest.host', verbose_name='host'),
),
]
# Generated by Django 3.1.2 on 2021-03-16 07:07
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('apiTest', '0002_auto_20210219_1354'),
]
operations = [
migrations.AddField(
model_name='runapirecord',
name='name',
field=models.CharField(max_length=200, null=True, verbose_name='api名称'),
),
]
# Generated by Django 3.1.2 on 2021-04-07 01:59
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('apiTest', '0003_runapirecord_name'),
]
operations = [
migrations.AlterField(
model_name='api',
name='create_time',
field=models.DateTimeField(auto_now=True, verbose_name='创建时间'),
),
]
from django.db import models
from django.contrib.auth import get_user_model
users = get_user_model()
import datetime
#请求类型
HTTP_METHOD_CHOICE = (
('POST', 'POST'),
('GET', 'GET'),
('PUT', 'PUT'),
('DELETE', 'DELETE')
)
#数据类型
REQUEST_TYPE = (
('json', 'json'),
('form-data', 'form-data')
)
class Project(models.Model):
"""
项目表
"""
PROJECTTYPE = [
['web','web'],
['app','app']
]
name = models.CharField(max_length=50, verbose_name='项目名称')
type = models.CharField(max_length=50, verbose_name='项目类型', choices=PROJECTTYPE)
description = models.CharField(max_length=1024, blank=True, null=True, verbose_name='描述')
create_time = models.DateTimeField(auto_now=True, verbose_name='更新时间')
found_time = models.DateTimeField(auto_now_add=datetime.datetime.now().replace(microsecond=0),verbose_name='创建时间')
# user = models.ForeignKey(users, on_delete=models.SET_NULL, null=True, verbose_name='创建人')
class Meta:
db_table = 'fusion_project'
verbose_name = "项目表"
verbose_name_plural = verbose_name
def __str__(self):
return "{}".format(self.name)
class Host(models.Model):
"""
host域名
"""
name = models.CharField(max_length=50, verbose_name='名称')
host = models.CharField(max_length=1024, verbose_name='Host地址')
description = models.CharField(max_length=1024, blank=True, null=True, verbose_name='描述')
project= models.ForeignKey(Project, on_delete=models.CASCADE, verbose_name='所属项目', related_name='host_list')
create_time = models.DateTimeField(auto_now=True, verbose_name='更新时间')
found_time = models.DateTimeField(auto_now_add=True, verbose_name='创建时间')
class Meta:
db_table="fusion_host"
verbose_name="host域名"
verbose_name_plural = verbose_name
def __str__(self):
return "{}".format(self.host)
class Api(models.Model):
"""
接口信息
"""
STATUS_CODE_CHOICE = (
('200', '200'),
('201', '201'),
('202', '202'),
('203', '203'),
('204', '204'),
('301', '301'),
('302', '302'),
('400', '400'),
('401', '401'),
('403', '403'),
('404', '404'),
('405', '405'),
('406', '406'),
('407', '407'),
('408', '408'),
('500', '500'),
('502', '502')
)
name = models.CharField(max_length=50, verbose_name='接口名称')
http_method = models.CharField(max_length=50, verbose_name='请求方式', choices=HTTP_METHOD_CHOICE)
host = models.ForeignKey(Host,on_delete=models.CASCADE,verbose_name='host',related_name='host_api',null=True)
project = models.ForeignKey(Project, on_delete=models.CASCADE, verbose_name='项目', related_name='api_list',null=True)
path = models.CharField(max_length=1024, verbose_name='接口地址')
headers = models.TextField(null=True,blank=True,verbose_name='请求头')
request_type = models.CharField(max_length=20,choices=REQUEST_TYPE,verbose_name='请求类型',default='form-data')
data = models.TextField(null=True,blank=True,verbose_name='提交的数据')
description = models.CharField(max_length=1024, blank=True, null=True, verbose_name='描述')
expect_code = models.CharField(default=200,max_length=10,verbose_name='期望返回的code',choices=STATUS_CODE_CHOICE)
expect_content = models.CharField(null=True,max_length=200,verbose_name='期望返回的内容',blank=True)
create_time = models.DateTimeField(auto_now=True, verbose_name='创建时间')
class Meta:
db_table = "fusion_api"
verbose_name="接口信息"
verbose_name_plural=verbose_name
def __str__(self):
return "{}".format(self.name)
class ApiArgument(models.Model):
"""
api的全局参数
"""
api = models.ForeignKey(Api, on_delete=models.CASCADE, verbose_name='用例', null=True,related_name='arguments')
name = models.CharField(max_length=100,null=True,verbose_name='参数名字')
value = models.CharField(max_length=100,null=True,verbose_name='参数的值')
class Meta:
db_table = "fusion_api_argument"
verbose_name="api的参数"
verbose_name_plural=verbose_name
def __str__(self):
return "{}".format(self.api)
class ApiArgumentExtract(models.Model):
"""
用例API的响应参数提取
"""
ARGUMENT_ORIGIN_CHOICE = (
('HEADER', 'HEADER'),#headers提取
('BODY', 'BODY'),#响应内容提取
('COOKIE', 'COOKIE')#cookie提取
)
api = models.ForeignKey(Api,on_delete=models.CASCADE,verbose_name='用例API',null=True,related_name='argumentExtract')
name = models.CharField(max_length=100,null=True,verbose_name='参数名字')
origin = models.CharField(max_length=20,null=True,choices=ARGUMENT_ORIGIN_CHOICE,verbose_name='参数来源')
format = models.CharField(max_length=100,null=True,verbose_name='参数获取的格式')
class Meta:
db_table = "fusion_api_argument_extract"
verbose_name="用例API的参数提取"
verbose_name_plural=verbose_name
def __str__(self):
return "{}".format(self.api)
class RunApiRecord(models.Model):
"""
API运行记录
"""
name = models.CharField(max_length=200,verbose_name='api名称',null=True)
url = models.CharField(max_length=200,verbose_name='请求的url')
http_method = models.CharField(max_length=10, verbose_name='请求方式', choices=HTTP_METHOD_CHOICE)
data = models.TextField(null=True,verbose_name='提交的数据')
headers = models.TextField(null=True,verbose_name='提交的header')
create_time = models.DateTimeField(auto_now=True,verbose_name='运行的时间')
return_code = models.CharField(max_length=10,verbose_name='返回的code')
return_content = models.TextField(null=True, verbose_name='返回的内容')
return_cookies = models.TextField(null=True, verbose_name='返回的cookies')
return_headers = models.TextField(null=True, verbose_name='返回的headers')
api = models.ForeignKey(Api,on_delete=models.CASCADE,verbose_name='关联的API',null=True)
assert_result = models.CharField(max_length=10,null=True,verbose_name='断言结果')
class Meta:
ordering = ['-create_time']
db_table = "fusion_run_api_record"
verbose_name="api运行记录"
verbose_name_plural=verbose_name
def __str__(self):
return "{}".format(self.url)
# -*-coding:utf-8 -*-
# __author__ = 'wuhongbin'
# Time:2021/1/25 11:13 上午
from .models import *
from rest_framework import serializers
from apps.users.serializers import UserSerializer
from rest_framework.serializers import ValidationError
from django.db import transaction
from rest_framework import validators
class HostSerializer(serializers.ModelSerializer):
"""
host域名
"""
project_id = serializers.IntegerField()
class Meta:
model = Host
fields = ['id','name','description','project_id','host']
extra_kwargs={
"name":{
'required': True
}
}
class ApiArgumentSerializer(serializers.ModelSerializer):
"""
api的全局参数
"""
class Meta:
model = ApiArgument
# fields = "__all__"
fields = ['name','value']
class ApiArgumentExtractSerializer(serializers.ModelSerializer):
"""
用例API的响应参数提取
"""
class Meta:
model = ApiArgumentExtract
fields = ['name','origin','format']
class ApiSerializer(serializers.ModelSerializer):
"""
API
"""
project_id = serializers.IntegerField(write_only=True)
host_id = serializers.IntegerField(write_only=True)
arguments = ApiArgumentSerializer(many=True)
argumentExtract = ApiArgumentExtractSerializer(many=True)
# arguments = ApiArgumentSerializer(read_only=True,many=True)
# argumentExtract = ApiArgumentExtractSerializer(read_only=True,many=True)
class Meta:
model = Api
fields = "__all__"
extra_kwargs = {
'name': {
'required': True, # 设置name字段必填
'min_length': 2,
'error_messages': {
'required': '必填项',
'min_length': '太短',
}
}
}
def validate_expect_content(self,value):
if len(value)>0:
if "=" not in value:
raise ValidationError({'expect_content': '预期结果格式错误'})
else:
return value
def create(self, validated_data):
arguments_list = validated_data.pop('arguments')
argumentExtract_list = validated_data.pop('argumentExtract')
api = Api.objects.create(**validated_data)
#保存api的全局参数
for arguments in arguments_list:
ApiArgument.objects.create(api=api,**arguments)
#保存用例API的响应参数提取
for argumentExtract in argumentExtract_list:
ApiArgumentExtract.objects.create(api=api,**argumentExtract)
return api
def update(self, instance, validated_data):
try:
with transaction.atomic():
# 更新api
arguments_list = validated_data.pop('arguments')
argumentExtract_list = validated_data.pop('argumentExtract')
instance.name = validated_data.get('name', instance.name)
instance.http_method = validated_data.get('http_method', instance.http_method)
instance.host = validated_data.get('host', instance.host)
instance.path = validated_data.get('path', instance.path)
instance.request_type = validated_data.get('request_type', instance.request_type)
instance.data = validated_data.get('data', instance.data)
instance.description = validated_data.get('description', instance.description)
instance.expect_code = validated_data.get('expect_code', instance.expect_code)
instance.expect_content = validated_data.get('expect_content', instance.expect_content)
instance.save()
# 删除api的全局参数和用例API的响应参数提取再新增
ApiArgument.objects.filter(api=instance).delete()
for arguments in arguments_list:
ApiArgument.objects.create(api=instance, **arguments)
ApiArgumentExtract.objects.filter(api=instance).delete()
for argumentExtract in argumentExtract_list:
ApiArgumentExtract.objects.create(api=instance, **argumentExtract)
return instance
except Exception as e:
return e
# 局部钩子
# def validate_name(self, value):
# if Api.objects.filter(name=value):
# raise ValidationError({'name': '该名称已存在'})
# return value
class RunApiRecordSerializer(serializers.ModelSerializer):
"""
API运行记录
"""
# api = ApiSerializer()
class Meta:
model = RunApiRecord
fields = "__all__"
class ProjectSerializer(serializers.ModelSerializer):
"""
项目表
"""
name = serializers.CharField(max_length=50, label="项目名称", help_text='项目名称',
validators=[validators.UniqueValidator(queryset=Project.objects.all(), message="项目名称重复")])
class Meta:
model = Project
fields = ['id','name','type','description']
extra_kwargs={
"name":{
'required': True
}
}
from django.test import TestCase
import os
# Create your tests here.
import requests
from urllib import parse
import json
import re
from requests.packages.urllib3.exceptions import InsecureRequestWarning
requests.packages.urllib3.disable_warnings(InsecureRequestWarning)
from lwjTest.settings import logger
def _replace_argument(target_str,arguments):
"""
:param target_str: {{变量}}
:param arguments: 需要替换的数据
:return:
"""
if not arguments:
return target_str
while True:
search_result = re.search(r"{{(.+?)}}",target_str)
if not search_result:
break
argument_name = search_result.group(1)
if argument_name in arguments:
target_str = re.sub("{{"+argument_name+"}}",arguments[argument_name],target_str)
else:
target_str = re.sub("{{"+argument_name+"}}",argument_name,target_str)
return target_str
def test1():
data_dict = {}
data={"id":"ceshi"}
a={"name":"test","value":"{{id}}"}
for key,value in a.items():
data_dict[key]=_replace_argument(value,data)
print(data_dict)
# -*-coding:utf-8 -*-
# __author__ = 'wuhongbin'
# Time:2021/1/25 11:13 上午
from django.urls import path,include,re_path
from . import views
from rest_framework.routers import DefaultRouter
rouer = DefaultRouter()
#项目
rouer.register('project',views.ProjectViewsets,basename='project')
#host
rouer.register('host',views.HostViewSets,basename='host')
#api
rouer.register('api',views.ApiViewsets,basename='api')
urlpatterns = [
#数据统计
re_path('^data/$',views.DataCountView.as_view()),
#项目---筛选查询
path('project',views.ProjectViewsets.as_view({"get":"query_name"})),
# host---筛选查询
path('host', views.HostViewSets.as_view({"get": "query_name"})),
# api---筛选查询
path('api', views.ApiViewsets.as_view({"get": "query_name"})),
#运行api
path('run/api/<int:api_id>/',views.RunApiRecordAPIView.as_view()),
# 导出api
path('export/api/', views.ApiDumpView.as_view()),
]+rouer.urls
\ No newline at end of file
This diff is collapsed.
default_app_config = 'apps.case.apps.CaseConfig'
from django.contrib import admin
from django.contrib import admin
from .models import *
@admin.register(Case)
class ProjectAdmin(admin.ModelAdmin):
"""自动化测试用例"""
list_display = ['name','description']
@admin.register(CaseApiList)
class ProjectAdmin(admin.ModelAdmin):
"""case中的api_list"""
list_display = ['api','case','index','reset_data','reset_expect_content','reset_expect_code']
@admin.register(CaseRunRecord)
class ProjectAdmin(admin.ModelAdmin):
"""用例运行记录"""
list_display = ['case','create_time']
@admin.register(CaseApiRunRecord)
class ProjectAdmin(admin.ModelAdmin):
"""Case API运行记录"""
list_display = ['name','url','http_method','create_time']
#
from django.apps import AppConfig
class CaseConfig(AppConfig):
name = 'apps.case'
verbose_name = '用例'
# Generated by Django 3.1.2 on 2021-02-19 05:54
from django.db import migrations, models
import django.db.models.deletion
class Migration(migrations.Migration):
initial = True
dependencies = [
('apiTest', '0002_auto_20210219_1354'),
]
operations = [
migrations.CreateModel(
name='Case',
fields=[
('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('name', models.CharField(max_length=50, verbose_name='用例名称')),
('description', models.CharField(blank=True, max_length=1024, null=True, verbose_name='描述')),
('create_time', models.DateTimeField(auto_now=True, verbose_name='更新时间')),
],
options={
'verbose_name': '自动化测试用例',
'verbose_name_plural': '自动化测试用例',
'db_table': 'fusion_case',
},
),
migrations.CreateModel(
name='CaseRunRecord',
fields=[
('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('create_time', models.DateTimeField(auto_now=True, verbose_name='运行时间')),
('case', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='case.case', verbose_name='所属用例')),
],
options={
'verbose_name': '用例运行记录',
'verbose_name_plural': '用例运行记录',
'db_table': 'fusion_case_run_record',
'ordering': ['-create_time'],
},
),
migrations.CreateModel(
name='CaseApiRunRecord',
fields=[
('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('name', models.CharField(max_length=200, verbose_name='api名称')),
('url', models.CharField(max_length=200, verbose_name='请求的url!')),
('http_method', models.CharField(choices=[('POST', 'POST'), ('GET', 'GET'), ('PUT', 'PUT'), ('DELETE', 'DELETE')], max_length=10, verbose_name='请求方式')),
('headers', models.TextField(null=True, verbose_name='请求头')),
('data', models.TextField(null=True, verbose_name='提交的数据')),
('create_time', models.DateTimeField(auto_now=True, verbose_name='运行的时间')),
('return_code', models.CharField(max_length=10, verbose_name='响应状态码')),
('return_content', models.TextField(null=True, verbose_name='响应内容')),
('return_time', models.CharField(max_length=1000, verbose_name='响应时间')),
('api', models.ForeignKey(null=True, on_delete=django.db.models.deletion.CASCADE, to='apiTest.api', verbose_name='关联的API')),
('case_record', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='api_records', to='case.caserunrecord', verbose_name='关联的case_record')),
],
options={
'verbose_name': 'Case API运行记录',
'verbose_name_plural': 'Case API运行记录',
'db_table': 'fusion_case_api_run_record',
},
),
migrations.CreateModel(
name='CaseApiList',
fields=[
('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('index', models.IntegerField(null=True, verbose_name='执行顺序')),
('reset_data', models.TextField(blank=True, null=True, verbose_name='提交的数据')),
('reset_expect_content', models.CharField(blank=True, max_length=200, null=True, verbose_name='期望返回的内容')),
('reset_expect_data', models.CharField(blank=True, max_length=200, null=True, verbose_name='请求参数')),
('reset_expect_code', models.CharField(blank=True, max_length=200, null=True, verbose_name='响应状态码')),
('api', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='apiTest.api')),
('case', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='case.case')),
],
options={
'verbose_name': '测试用例中的api',
'verbose_name_plural': '测试用例中的api',
'db_table': 'fusion_case_api_list',
'managed': True,
},
),
migrations.AddField(
model_name='case',
name='api_list',
field=models.ManyToManyField(related_name='case_list', through='case.CaseApiList', to='apiTest.Api'),
),
]
# Generated by Django 3.1.2 on 2021-02-19 05:58
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('case', '0001_initial'),
]
operations = [
migrations.RemoveField(
model_name='caseapilist',
name='reset_expect_data',
),
migrations.AlterField(
model_name='caseapilist',
name='reset_expect_code',
field=models.CharField(blank=True, max_length=200, null=True, verbose_name='预期状态码'),
),
]
# Generated by Django 3.1.2 on 2021-02-19 06:30
from django.db import migrations, models
import django.db.models.deletion
class Migration(migrations.Migration):
dependencies = [
('apiTest', '0002_auto_20210219_1354'),
('case', '0002_auto_20210219_1358'),
]
operations = [
migrations.RemoveField(
model_name='case',
name='api_list',
),
migrations.AlterField(
model_name='caseapilist',
name='api',
field=models.ForeignKey(null=True, on_delete=django.db.models.deletion.CASCADE, to='apiTest.api'),
),
migrations.AlterField(
model_name='caseapilist',
name='case',
field=models.ForeignKey(null=True, on_delete=django.db.models.deletion.CASCADE, to='case.case'),
),
]
# Generated by Django 3.1.2 on 2021-02-25 06:19
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('apiTest', '0002_auto_20210219_1354'),
('case', '0003_auto_20210219_1430'),
]
operations = [
migrations.AddField(
model_name='case',
name='api_list',
field=models.ManyToManyField(related_name='case_list', through='case.CaseApiList', to='apiTest.Api'),
),
]
# Generated by Django 3.1.2 on 2021-03-02 08:35
from django.db import migrations
class Migration(migrations.Migration):
dependencies = [
('case', '0004_case_api_list'),
]
operations = [
migrations.RemoveField(
model_name='case',
name='api_list',
),
]
# Generated by Django 3.1.2 on 2021-04-08 09:15
from django.db import migrations, models
import django.db.models.deletion
class Migration(migrations.Migration):
dependencies = [
('case', '0005_remove_case_api_list'),
]
operations = [
migrations.AlterField(
model_name='caseapilist',
name='case',
field=models.ForeignKey(null=True, on_delete=django.db.models.deletion.CASCADE, related_name='api_list', to='case.case'),
),
]
from django.db import models
from ..apiTest.models import Api
class Case(models.Model):
"""
自动化测试用例
"""
name = models.CharField(max_length=50, verbose_name='用例名称')
# api_list = models.ManyToManyField(Api,related_name='case_list',through='CaseApiList')
description = models.CharField(max_length=1024, blank=True, null=True, verbose_name='描述')
create_time = models.DateTimeField(auto_now=True, verbose_name='更新时间')
class Meta:
db_table = "fusion_case"
verbose_name="自动化测试用例"
verbose_name_plural=verbose_name
def __str__(self):
return "{}".format(self.name)
class CaseApiList(models.Model):
"""
case中的api_list
"""
api = models.ForeignKey(Api,on_delete=models.CASCADE,null=True)
case = models.ForeignKey(Case,on_delete=models.CASCADE,null=True,related_name='api_list')
#添加的字段
index =models.IntegerField(verbose_name="执行顺序",null=True)
reset_data=models.TextField(null=True,blank=True,verbose_name='提交的数据')
reset_expect_content = models.CharField(null=True, max_length=200, verbose_name='期望返回的内容', blank=True)
reset_expect_code = models.CharField(null=True, max_length=200, verbose_name='预期状态码', blank=True)
class Meta:
managed = True
db_table = "fusion_case_api_list"
verbose_name="测试用例中的api"
verbose_name_plural=verbose_name
class CaseRunRecord(models.Model):
"""
用例运行记录
"""
case = models.ForeignKey(Case,on_delete=models.CASCADE,verbose_name='所属用例')
create_time = models.DateTimeField(auto_now=True,verbose_name='运行时间')
class Meta:
ordering = ['-create_time']
db_table = "fusion_case_run_record"
verbose_name="用例运行记录"
verbose_name_plural=verbose_name
def __str__(self):
return "{}".format(self.case)
class CaseApiRunRecord(models.Model):
"""
Case API运行记录
"""
HTTP_METHOD_CHOICE = (
('POST', 'POST'),
('GET', 'GET'),
('PUT', 'PUT'),
('DELETE', 'DELETE')
)
name = models.CharField(max_length=200, verbose_name='api名称')
url = models.CharField(max_length=200, verbose_name='请求的url!')
http_method = models.CharField(max_length=10, verbose_name='请求方式', choices=HTTP_METHOD_CHOICE)
headers = models.TextField(null=True,verbose_name='请求头')
data = models.TextField(null=True, verbose_name='提交的数据')
create_time = models.DateTimeField(auto_now=True, verbose_name='运行的时间')
return_code = models.CharField(max_length=10, verbose_name='响应状态码')
return_content = models.TextField(null=True, verbose_name='响应内容')
return_time = models.CharField(max_length=1000, verbose_name='响应时间')
api = models.ForeignKey(Api, on_delete=models.CASCADE, verbose_name='关联的API', null=True)
case_record = models.ForeignKey(CaseRunRecord, on_delete=models.CASCADE, verbose_name='关联的case_record',
related_name='api_records')
class Meta:
db_table = "fusion_case_api_run_record"
verbose_name="Case API运行记录"
verbose_name_plural=verbose_name
def __str__(self):
return "{}".format(self.url)
This diff is collapsed.
# -*-coding:utf-8 -*-
# __author__ = 'wuhongbin'
# Time:2021/2/19 11:23 上午
from .models import Case,CaseApiList,CaseRunRecord,CaseApiRunRecord
from ..apiTest.serializers import ApiSerializer
from rest_framework import serializers
class CaseApiListSerializer(serializers.ModelSerializer):
"""
case中的api_list
"""
class Meta:
model = CaseApiList
# fields = "__all__"
exclude = ('id',)
class CaseSerializer(serializers.ModelSerializer):
"""
自动化测试用例
"""
api_list = CaseApiListSerializer(many=True)
class Meta:
model = Case
fields = "__all__"
# fields = ['id','name','api_list']
def create(self, validated_data):
api_list = validated_data.pop('api_list')
case = Case.objects.create(**validated_data)
for api in api_list:
CaseApiList.objects.create(case=case,**api)
return case
def update(self,instance, validated_data):
api_list = validated_data.pop('api_list')
instance.name = validated_data.get('name', instance.name)
instance.description = validated_data.get('description', instance.description)
instance.save()
CaseApiList.objects.filter(case=instance).delete()
for api in api_list:
CaseApiList.objects.create(case=instance, **api)
return instance
class CaseApiRunRecordSerializer(serializers.ModelSerializer):
"""
case api运行记录
"""
api = ApiSerializer()
class Meta:
model = CaseApiRunRecord
fields = "__all__"
class CaseRunRecordSerializer(serializers.ModelSerializer):
"""
用例运行记录
"""
api_records = CaseApiRunRecordSerializer(many=True)
case = CaseSerializer()
class Meta:
model = CaseRunRecord
fields = "__all__"
from django.test import TestCase
def test1(number):
a = [i for i in range(number)]
for i in a:
print("%d "%i,end='')
test1(5)
\ No newline at end of file
# -*-coding:utf-8 -*-
# __author__ = 'wuhongbin'
# Time:2021/2/19 11:23 上午
from django.urls import path,include,re_path
from . import views
from rest_framework.routers import DefaultRouter
rouer = DefaultRouter()
#case
rouer.register('case',views.CaseViewsets,basename='case')
urlpatterns = [
#运行单条测试用例
path('run/<case_id>/',views.CaseRunApiView.as_view()),
#运行多条测试用例
re_path('runList/',views.CaseListApiRunRecordAPIView.as_view()),
#查看运行记录
path('record',views.RecordView.as_view()),
#测试用例---查询
path('case',views.CaseViewsets.as_view({"get":"query_name"})),
]+rouer.urls
\ No newline at end of file
from django.shortcuts import render
from .models import Case
from ..apiTest.models import RunApiRecord
from ..apiTest.serializers import RunApiRecordSerializer
from .serializers import CaseSerializer,CaseApiListSerializer,CaseRunRecordSerializer
from utils.apiResponse import ApiResponse
from utils.pagination import MyPageNumberPagination
from rest_framework.views import APIView
from rest_framework.viewsets import ModelViewSet
from rest_framework.decorators import action
from rest_framework.generics import ListCreateAPIView
from django.contrib.auth import get_user_model
from lwjTest.settings import logger
from .run_case import run_case,run_case_list
from .models import CaseRunRecord
from ..users.authorizations import JWTAuthentication
from ..users.permission import MyPermission
class CaseViewsets(ModelViewSet):
"""
测试用例
"""
queryset = Case.objects.all()
serializer_class = CaseSerializer
pagination_class = MyPageNumberPagination
permission_classes = [MyPermission]
authentication_classes = [JWTAuthentication]
#筛选
@action(methods=['get'],detail=False)
def query_name(self, request, *args,**kwargs):
case_name = Case.objects.filter(name__contains=self.request.query_params.get('name',''))
ser = CaseSerializer(case_name,many=True)
return ApiResponse(results=ser.data)
class CaseRunApiView(APIView):
permission_classes = [MyPermission]
authentication_classes = [JWTAuthentication]
"""
单条测试用例运行
"""
def post(self,request,case_id):
logger.info("执行测试用例id:{}".format(case_id))
run_case(case_id)
return ApiResponse(results="测试用例运行成功")
class CaseListApiRunRecordAPIView(APIView):
permission_classes = [MyPermission]
authentication_classes = [JWTAuthentication]
"""
批量运行测试用例
"""
def post(self, request, *args, **kwargs):
case_id_list = request.data.get("case_id_list")
run_case_list(case_id_list)
return ApiResponse(results="批量测试用例运行成功")
class RecordView(APIView):
"""
case运行记录
"""
pagination_class = MyPageNumberPagination
permission_classes = [MyPermission]
authentication_classes = [JWTAuthentication]
def get(self,request):
#通过type区分是api运行记录还是case运行记录
#通过id查询具体用例
type = request.GET.get('type')
project_id = request.GET.get('project')
logger.info("record_type:{}".format(type))
logger.info("project_id:{}".format(project_id))
if type == 'api':
#单查
if project_id:
records = RunApiRecord.objects.filter(id=project_id)
pg = MyPageNumberPagination()
page_records = pg.paginate_queryset(queryset=records,request=request,view=self)
serializer = RunApiRecordSerializer(instance=page_records,many=True)
return ApiResponse(results=serializer.data)
#群查
else:
records = RunApiRecord.objects.filter()
pg = MyPageNumberPagination()
page_records = pg.paginate_queryset(queryset=records,request=request,view=self)
serializer = RunApiRecordSerializer(instance=page_records,many=True).data
return pg.get_paginated_response(serializer)
elif type=="case":
#单查
if project_id:
records = CaseRunRecord.objects.filter(case__project_id=project_id)
pg = MyPageNumberPagination()
page_records = pg.paginate_queryset(queryset=records, request=request, view=self)
serializer = CaseRunRecordSerializer(instance=page_records, many=True).data
return pg.get_paginated_response(serializer)
else:
records = CaseRunRecord.objects.filter()
pg = MyPageNumberPagination()
page_records = pg.paginate_queryset(queryset=records, request=request, view=self)
serializer = CaseRunRecordSerializer(instance=page_records, many=True).data
return pg.get_paginated_response(serializer)
else:
return ApiResponse(results="参数错误")
\ No newline at end of file
default_app_config = 'apps.chanDao.apps.ChandaoConfig'
from django.contrib import admin
from .models import *
@admin.register(ChanDaoProject)
class ProjectAdmin(admin.ModelAdmin):
"""阐道-项目名称"""
list_display = ['project','product_person','test_person']
@admin.register(ChanDaoModular)
class ProjectAdmin(admin.ModelAdmin):
"""阐道-模块"""
list_display = ['project','modular']
@admin.register(ChanDaoCase)
class ProjectAdmin(admin.ModelAdmin):
"""阐道-用例"""
list_display = ['modular','title','case_type','case_stage','result','found_time']
@admin.register(ChanDaoCaseStep)
class ProjectAdmin(admin.ModelAdmin):
"""测试步骤"""
list_display = ['case','step','expect','case_result','remarks']
from django.apps import AppConfig
class ChandaoConfig(AppConfig):
name = 'apps.chanDao'
verbose_name = '阐道'
# Generated by Django 3.1.2 on 2021-02-23 08:02
from django.conf import settings
from django.db import migrations, models
import django.db.models.deletion
class Migration(migrations.Migration):
initial = True
dependencies = [
migrations.swappable_dependency(settings.AUTH_USER_MODEL),
]
operations = [
migrations.CreateModel(
name='ChanDaoProject',
fields=[
('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('project', models.CharField(max_length=30, verbose_name='项目名称')),
('product_person', models.CharField(max_length=10, verbose_name='产品负责人')),
('test_person', models.CharField(max_length=10, verbose_name='测试负责人')),
],
options={
'verbose_name': '阐道_项目名称',
'verbose_name_plural': '阐道_项目名称',
'db_table': 'fusion_chandao_project',
},
),
migrations.CreateModel(
name='ChanDaoModular',
fields=[
('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('modular', models.CharField(max_length=10, verbose_name='模块名称')),
('project', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='chanDao.chandaoproject')),
],
options={
'verbose_name': '阐道_项目模块',
'verbose_name_plural': '阐道_项目模块',
'db_table': 'fusion_chandao_modular',
},
),
migrations.CreateModel(
name='ChanDaoCase',
fields=[
('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('title', models.CharField(max_length=30, verbose_name='用例标题')),
('preconditions', models.CharField(max_length=200, verbose_name='前置条件')),
('case_step', models.CharField(max_length=3000, verbose_name='用例步骤')),
('case_type', models.CharField(choices=[['功能测试', '功能测试'], ['性能测试', '性能测试'], ['接口测试', '接口测试']], default='功能测试', max_length=10, verbose_name='用例类型')),
('case_stage', models.CharField(choices=[['功能测试阶段', '功能测试阶段'], ['系统测试阶段', '系统测试阶段'], ['冒烟测试阶段', '冒烟测试阶段']], default='功能测试阶段', max_length=10, verbose_name='适用阶段')),
('case_priority', models.IntegerField(choices=[['功能测试阶段', '功能测试阶段'], ['系统测试阶段', '系统测试阶段'], ['冒烟测试阶段', '冒烟测试阶段']], default=3, max_length=10, verbose_name='优先级')),
('remarks', models.CharField(max_length=3000, verbose_name='备注')),
('result', models.CharField(choices=[['pass', 'pass'], ['fail', 'fail'], ['block', 'block']], default='pass', max_length=3000, verbose_name='用例结果')),
('create_time', models.DateTimeField(auto_now=True, verbose_name='更新时间')),
('found_time', models.DateTimeField(auto_now_add=True, verbose_name='创建时间')),
('modular', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='chanDao.chandaomodular')),
('user', models.ForeignKey(null=True, on_delete=django.db.models.deletion.SET_NULL, to=settings.AUTH_USER_MODEL, verbose_name='创建人')),
],
options={
'verbose_name': '阐道_用例',
'verbose_name_plural': '阐道_用例',
'db_table': 'fusion_chandao_case',
},
),
]
# Generated by Django 3.1.2 on 2021-02-23 09:34
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('chanDao', '0001_initial'),
]
operations = [
migrations.AlterField(
model_name='chandaocase',
name='result',
field=models.CharField(choices=[['unexecuted', 'unexecuted'], ['pass', 'pass'], ['fail', 'fail'], ['block', 'block']], default='unexecuted', max_length=3000, verbose_name='用例结果'),
),
]
# Generated by Django 3.1.2 on 2021-02-24 02:51
from django.db import migrations, models
import django.db.models.deletion
class Migration(migrations.Migration):
dependencies = [
('chanDao', '0002_auto_20210223_1734'),
]
operations = [
migrations.CreateModel(
name='ChanDaoCaseStep',
fields=[
('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('step', models.CharField(max_length=3000, verbose_name='')),
('expect', models.CharField(max_length=3000, verbose_name='')),
('case_result', models.CharField(choices=[['unexecuted', 'unexecuted'], ['pass', 'pass'], ['fail', 'fail'], ['block', 'block']], default='unexecuted', max_length=3000, verbose_name='用例结果')),
('remarks', models.CharField(max_length=3000, verbose_name='')),
('case', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='chanDao.chandaocase')),
],
options={
'verbose_name': '阐道_用例步骤',
'verbose_name_plural': '阐道_用例步骤',
'db_table': 'fusion_chandao_step',
},
),
]
# Generated by Django 3.1.2 on 2021-02-24 06:46
from django.db import migrations, models
import django.db.models.deletion
class Migration(migrations.Migration):
dependencies = [
('chanDao', '0003_chandaocasestep'),
]
operations = [
migrations.RemoveField(
model_name='chandaocase',
name='case_step',
),
migrations.AlterField(
model_name='chandaocase',
name='result',
field=models.CharField(choices=[['unexecuted', 'unexecuted'], ['pass', 'pass'], ['fail', 'fail']], default='unexecuted', max_length=3000, verbose_name='用例结果'),
),
migrations.AlterField(
model_name='chandaocasestep',
name='case',
field=models.ForeignKey(null=True, on_delete=django.db.models.deletion.CASCADE, related_name='case', to='chanDao.chandaocase'),
),
]
# Generated by Django 3.1.2 on 2021-02-24 08:05
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('chanDao', '0004_auto_20210224_1446'),
]
operations = [
migrations.AlterField(
model_name='chandaocase',
name='case_priority',
field=models.IntegerField(choices=[['功能测试阶段', '功能测试阶段'], ['系统测试阶段', '系统测试阶段'], ['冒烟测试阶段', '冒烟测试阶段']], default='3', max_length=10, verbose_name='优先级'),
),
]
# Generated by Django 3.1.2 on 2021-02-24 08:06
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('chanDao', '0005_auto_20210224_1605'),
]
operations = [
migrations.AlterField(
model_name='chandaocase',
name='case_priority',
field=models.IntegerField(choices=[[1, 1], [2, 2], [3, 3], [4, 4]], max_length=10, verbose_name='优先级'),
),
]
# Generated by Django 3.1.2 on 2021-02-25 06:19
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('chanDao', '0006_auto_20210224_1606'),
]
operations = [
migrations.AlterField(
model_name='chandaocase',
name='case_priority',
field=models.IntegerField(choices=[[1, 1], [2, 2], [3, 3], [4, 4]], verbose_name='优先级'),
),
migrations.AlterField(
model_name='chandaocasestep',
name='expect',
field=models.CharField(max_length=3000, verbose_name='预期'),
),
migrations.AlterField(
model_name='chandaocasestep',
name='remarks',
field=models.CharField(max_length=3000, verbose_name='备注'),
),
migrations.AlterField(
model_name='chandaocasestep',
name='step',
field=models.CharField(max_length=3000, verbose_name='步骤'),
),
]
# Generated by Django 3.1.2 on 2021-03-01 02:08
from django.db import migrations, models
import django.db.models.deletion
class Migration(migrations.Migration):
dependencies = [
('chanDao', '0007_auto_20210225_1419'),
]
operations = [
migrations.AlterField(
model_name='chandaocase',
name='modular',
field=models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='chanDao.chandaomodular', verbose_name='模块'),
),
migrations.AlterField(
model_name='chandaocasestep',
name='remarks',
field=models.CharField(max_length=3000, null=True, verbose_name='备注'),
),
]
# Generated by Django 3.1.2 on 2021-03-02 08:52
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('chanDao', '0008_auto_20210301_1008'),
]
operations = [
migrations.AlterField(
model_name='chandaocasestep',
name='remarks',
field=models.CharField(blank=True, max_length=3000, null=True, verbose_name='备注'),
),
]
# Generated by Django 3.1.2 on 2021-04-08 09:15
from django.db import migrations
class Migration(migrations.Migration):
dependencies = [
('chanDao', '0009_auto_20210302_1652'),
]
operations = [
migrations.AlterModelOptions(
name='chandaoproject',
options={'ordering': ['id'], 'verbose_name': '阐道_项目名称', 'verbose_name_plural': '阐道_项目名称'},
),
]
# Generated by Django 3.1.2 on 2022-04-07 02:12
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('chanDao', '0010_auto_20210408_1715'),
]
operations = [
migrations.AlterField(
model_name='chandaomodular',
name='modular',
field=models.CharField(max_length=20, verbose_name='模块名称'),
),
migrations.AlterField(
model_name='chandaoproject',
name='product_person',
field=models.CharField(max_length=20, verbose_name='产品负责人'),
),
migrations.AlterField(
model_name='chandaoproject',
name='test_person',
field=models.CharField(max_length=20, verbose_name='测试负责人'),
),
]
from django.db import models
from django.contrib.auth import get_user_model
users = get_user_model()
class ChanDaoProject(models.Model):
"""
阐道-项目名称
"""
project = models.CharField(max_length=30,verbose_name="项目名称")
product_person = models.CharField(max_length=20, verbose_name="产品负责人")
test_person = models.CharField(max_length=20, verbose_name="测试负责人")
class Meta:
db_table = "fusion_chandao_project"
verbose_name="阐道_项目名称"
verbose_name_plural=verbose_name
ordering = ['id']
def __str__(self):
return "{}".format(self.project)
class ChanDaoModular(models.Model):
"""
阐道-模块
"""
project = models.ForeignKey(ChanDaoProject,on_delete=models.CASCADE)
modular = models.CharField(max_length=20,verbose_name="模块名称")
class Meta:
db_table = "fusion_chandao_modular"
verbose_name="阐道_项目模块"
verbose_name_plural=verbose_name
def __str__(self):
return "{}".format(self.modular)
class ChanDaoCase(models.Model):
"""
阐道-用例
"""
CASE_TYPE = [
['功能测试', '功能测试'],
['性能测试', '性能测试'],
['接口测试', '接口测试']
]
CASE_STAGE = [
['功能测试阶段', '功能测试阶段'],
['系统测试阶段', '系统测试阶段'],
['冒烟测试阶段', '冒烟测试阶段'],
]
CASE_PRIORITY = [
[1,1],
[2,2],
[3,3],
[4,4],
]
RESULT = [
['unexecuted', 'unexecuted'], # 未执行
['pass', 'pass'], # 通过
['fail', 'fail'], # 失败
]
modular = models.ForeignKey(ChanDaoModular,on_delete=models.CASCADE,verbose_name="模块")
title = models.CharField(max_length=30,verbose_name="用例标题")
preconditions = models.CharField(max_length=200,verbose_name="前置条件")
case_type = models.CharField(max_length=10,verbose_name="用例类型",choices=CASE_TYPE,default='功能测试')
case_stage = models.CharField(max_length=10,verbose_name="适用阶段",choices=CASE_STAGE,default='功能测试阶段')
case_priority = models.IntegerField(verbose_name="优先级",choices=CASE_PRIORITY)
remarks = models.CharField(max_length=3000,verbose_name="备注")
user = models.ForeignKey(users, on_delete=models.SET_NULL, null=True, verbose_name='创建人')
result = models.CharField(max_length=3000,verbose_name="用例结果",choices=RESULT,default='unexecuted')
create_time = models.DateTimeField(auto_now=True, verbose_name='更新时间')
found_time = models.DateTimeField(auto_now_add=True, verbose_name='创建时间')
class Meta:
db_table = "fusion_chandao_case"
verbose_name="阐道_用例"
verbose_name_plural=verbose_name
def __str__(self):
return "{}".format(self.title)
class ChanDaoCaseStep(models.Model):
"""
测试步骤
"""
RESULT = [
['unexecuted', 'unexecuted'], # 未执行
['pass', 'pass'], # 通过
['fail', 'fail'], # 失败
['block','block'], #阻塞
]
case = models.ForeignKey(ChanDaoCase,on_delete=models.CASCADE,null=True,related_name="case")
step = models.CharField(max_length=3000,verbose_name="步骤")
expect = models.CharField(max_length=3000,verbose_name="预期")
case_result = models.CharField(max_length=3000,verbose_name="用例结果",choices=RESULT,default='unexecuted')
remarks = models.CharField(max_length=3000,blank=True,null=True,verbose_name="备注")
class Meta:
db_table = "fusion_chandao_step"
verbose_name="阐道_用例步骤"
verbose_name_plural=verbose_name
def __str__(self):
return "{}".format(self.case)
# -*-coding:utf-8 -*-
# __author__ = 'wuhongbin'
# Time:2021/2/20 2:02 下午
from rest_framework import serializers
from .models import ChanDaoModular,ChanDaoCase,ChanDaoProject,ChanDaoCaseStep
class ChanDaoProjectSerializer(serializers.ModelSerializer):
"""
阐道-项目名称
"""
modular_count = serializers.IntegerField(read_only=True,label="模块的用例数量")
class Meta:
model = ChanDaoProject
fields = "__all__"
class ChanDaoModularSerializer(serializers.ModelSerializer):
"""
阐道-项目模块
"""
case_count = serializers.IntegerField(read_only=True,label="总用例")
case_unexecuted_count = serializers.IntegerField(read_only=True,label="未执行用例")
case_pass_count = serializers.IntegerField(read_only=True, label="通过用例")
case_fail_count = serializers.IntegerField(read_only=True, label="失败用例")
case_block_count = serializers.IntegerField(read_only=True, label="阻塞用例")
class Meta:
model = ChanDaoModular
fields = "__all__"
class ChanDaoCaseStepSerializer(serializers.ModelSerializer):
"""
阐道-用例步骤
"""
class Meta:
model = ChanDaoCaseStep
fields = "__all__"
class ChanDaoCaseSerializer(serializers.ModelSerializer):
"""
阐道-用例
"""
case = ChanDaoCaseStepSerializer(many=True)
class Meta:
model = ChanDaoCase
fields = "__all__"
def create(self, validated_data):
case_setp_list = validated_data.pop("case")
case = ChanDaoCase.objects.create(**validated_data)
for case_setp in case_setp_list:
ChanDaoCaseStep.objects.create(case=case,**case_setp)
return case
def update(self, instance, validated_data):
case_step_list = validated_data.pop("case")
instance.preconditions = validated_data.get('preconditions', instance.preconditions)
instance.case_type = validated_data.get('case_type', instance.case_type)
instance.case_stage = validated_data.get('case_stage', instance.case_stage)
instance.case_priority = validated_data.get('case_priority', instance.case_priority)
instance.remarks = validated_data.get('remarks', instance.remarks)
instance.result = validated_data.get('result', instance.result)
instance.modular = validated_data.get('modular', instance.modular)
instance.user = validated_data.get('user', instance.user)
instance.save()
ChanDaoCaseStep.objects.filter(case=instance).delete()
for case_step in case_step_list:
ChanDaoCaseStep.objects.create(case=instance, **case_step)
return instance
from django.test import TestCase
# -*-coding:utf-8 -*-
# __author__ = 'wuhongbin'
# Time:2021/2/20 2:02 下午
from django.urls import path,include,re_path
from . import views
from rest_framework.routers import DefaultRouter
rouer = DefaultRouter()
#阐道---项目
rouer.register('project',views.ChanDaoProjectViewSet,basename='ChanDaoProject')
rouer.register('modular',views.ChanDaoModularViewSet,basename='ChanDaoModular')
rouer.register('case',views.ChanDaoCaseViewSet,basename='ChanDaoCase')
urlpatterns = [
#导出测试用例
path('export/case/<pk>/', views.CaseDumpView.as_view()),
path('export/case/', views.CaseDumpView.as_view()),
#下载测试用例模版
path('excel/down/', views.ExcelDownload.as_view()),
#导入测试用例
path('import/case/',views.CaseImport.as_view()),
#某个项目下的模块列表
path('project_modular/<project_id>/',
views.ChanDaoModularViewSet.as_view({"get": "project_modular"})),
#某个模块下的用例列表
path('modular_case/<modular_id>/',
views.ChanDaoCaseViewSet.as_view({"get": "modular_case"})),
#运行用例
path('case/result/<case_id>/',views.CaseResult.as_view()),
#数据统计
path('data/count/',views.DataCountView.as_view()),
]+rouer.urls
This diff is collapsed.
from django.contrib import admin
# Register your models here.
from django.contrib import admin
from .models import *
@admin.register(GenerateCaseName)
class ProjectAdmin(admin.ModelAdmin):
"""导入用例名称"""
list_display = ['name','is_delete']
@admin.register(GenerateCase)
class ProjectAdmin(admin.ModelAdmin):
"""解析测试用例"""
list_display = ['host','path','method','request_type']
@admin.register(GenerateCaseRunRecord)
class ProjectAdmin(admin.ModelAdmin):
"""导入的用例运行记录"""
list_display = ['name','create_time']
@admin.register(GenerateRunStepRecord)
class ProjectAdmin(admin.ModelAdmin):
"""导入的用例运行步骤记录"""
list_display = ['url','http_method','data']
This diff is collapsed.
from django.apps import AppConfig
class GeneratecaseConfig(AppConfig):
name = 'apps.generateCase'
# -*-coding:utf-8 -*-
# __author__ = 'wuhongbin'
# Time:2021/5/21 2:37 下午
import random
import re
import json
import datetime
import hashlib
import string
from faker import Faker
from decimal import Context,ROUND_HALF_UP
class DataFunction():
fake = Faker(locale='zh_CN')
def md5(self, key):
"""
md5加密
:param key:
:return:
"""
result = hashlib.md5()
result.update(key.encode("utf-8"))
result_code = result.hexdigest()
return result_code.lower()
def randomstr(self, length):
'''
生成指定长度大小写字母组合
:param length:
:return:
'''
return ''.join(random.sample(string.ascii_letters, length)).lower()
def randomstrInt(self, length):
'''
生成指定长度随机数字和大小写字母组合
:param length:
:return:
'''
return ''.join(random.sample(string.ascii_letters + string.digits, length)).lower()
def randomint(self, length):
'''
生成指定长度随机数字
:param length:
:return:
'''
s = [str(i) for i in range(10)]
return ''.join(random.sample(s, length))
def randomWord(self, length):
"""
生成指定长度随机汉字
:param length:
:return:
"""
str_data = ""
for i in range(length):
head = random.randint(0xb0, 0xf7)
body = random.randint(0xa1, 0xfe)
val = f'{head:x} {body:x}'
str = bytes.fromhex(val).decode('gb2312')
str_data = ''.join([str_data, str])
return str_data
def times(self, day):
"""
获取时间
:param day: day<0:未来时间,day>0:过去时间
:return:
"""
today = datetime.datetime.now().replace(microsecond=0)
times = today - datetime.timedelta(days=day)
return str(times)
def phone_number(self):
"""
生成随机手机号码
:return:手机号码
"""
return self.fake.phone_number()
def email(self, *args, **kwargs):
"""
生成随机邮箱
:return:邮箱
"""
return self.fake.email(*args, **kwargs)
def data_parameterization(self, funcs):
"""
:param funcs:
:return:
"""
if funcs[0][0] == "md5":
original_data = self.md5(funcs[0][1])
elif funcs[0][0] == "str":
original_data = self.randomstr(int(funcs[0][1]))
elif funcs[0][0] == "int":
original_data = self.randomint(int(funcs[0][1]))
elif funcs[0][0] == "str_int":
original_data = self.randomstrInt(int(funcs[0][1]))
elif funcs[0][0] == "word":
original_data = self.randomWord(int(funcs[0][1]))
elif funcs[0][0] == "times":
original_data = self.times(int(funcs[0][1]))
print(original_data)
elif funcs[0][0] == "phone":
original_data = self.phone_number()
elif funcs[0][0] == "email":
print(funcs[0][1])
if len(funcs[0][1]) is None:
original_data = self.email()
else:
original_data = self.email((funcs[0][1]))
elif funcs[0][0] == "randomstrInt":
original_data = self.randomstrInt(int(funcs[0][1]))
return original_data
def get_num(self,num):
"""
处理精度丢失
:param num:
:return:
"""
num = str(num)
if float(num)>=1:
a = "%.2f" % float(num)
c = Context(prec=(len(a)-1), rounding=ROUND_HALF_UP).create_decimal(num)
return float(str(c))
if float(num)<1:
d = float(num)*100
if d-int(d)<0.5:
return float(int(d)/100)
else:
i = (int(d)+1)/100
return float(i)
if __name__ == '__main__':
pass
# print(DataFunction().data_parameterization(funcs))
# print(DataFunction().data_parameterization(funcs))
# print(DataFunction().email())
# print(DataFunction().times(-2))
# # original_data="___md5{5}"
# # original_data="___str{5}"
# original_data = "___int{5}"
# # 处理参数需要使用自定义方法
# from utils.primordial_sql import my_custom_sql
# sql="select data from fusion_generate_case where id=3238;"
# data=my_custom_sql(sql)[0][0]
# data_dict = json.loads(data)
# data1=dict()
# for key, value in data_dict.items():
# print(key,value)
# # 处理参数需要使用自定义方法
# if type(value)==str and "___" in value:
# FUNC_EXPR = '___(.*?){(.*?)}'
# funcs = re.findall(FUNC_EXPR, value)
# if funcs[0][0] == "md5":
# value = DataFunction().md5(funcs[0][1])
# elif funcs[0][0] == "str":
# value = DataFunction().randomstr(int(funcs[0][1]))
# elif funcs[0][0] == "int":
# value = DataFunction().randomint(int(funcs[0][1]))
# elif funcs[0][0] == "str_int":
# value = DataFunction().randomstrInt(int(funcs[0][1]))
# print(value)
# if "{{" not in data:
# data1[key] = value
# print(data1)
# Generated by Django 3.1.2 on 2021-04-23 02:56
from django.db import migrations, models
import django.db.models.deletion
class Migration(migrations.Migration):
initial = True
dependencies = [
]
operations = [
migrations.CreateModel(
name='GenerateCaseName',
fields=[
('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('name', models.CharField(max_length=100, verbose_name='用例名称')),
],
options={
'verbose_name': '导入用例名称',
'verbose_name_plural': '导入用例名称',
'db_table': 'fusion_generate_case_name',
},
),
migrations.CreateModel(
name='GenerateCase',
fields=[
('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('host', models.CharField(max_length=100, verbose_name='域名')),
('path', models.CharField(max_length=100, verbose_name='请求路径')),
('method', models.CharField(choices=[('POST', 'POST'), ('GET', 'GET'), ('PUT', 'PUT'), ('DELETE', 'DELETE')], max_length=100, verbose_name='请求方法')),
('request_type', models.CharField(choices=[('json', 'json'), ('form-data', 'form-data')], max_length=100, verbose_name='请求类型')),
('data', models.CharField(max_length=500, verbose_name='请求参数')),
('headers', models.CharField(max_length=100, verbose_name='请求头')),
('runTime', models.CharField(blank=True, max_length=100, verbose_name='请求时间')),
('argumentExtract', models.CharField(blank=True, max_length=100, verbose_name='参数提取')),
('expect_code', models.IntegerField(max_length=10, verbose_name='预期状态码')),
('expect_content', models.CharField(blank=True, max_length=200, verbose_name='断言内容')),
('create_time', models.DateTimeField(auto_now=True, verbose_name='创建时间')),
('name', models.ForeignKey(null=True, on_delete=django.db.models.deletion.CASCADE, to='generateCase.generatecasename', verbose_name='用例名称')),
],
options={
'verbose_name': '导入接口用例',
'verbose_name_plural': '导入接口用例',
'db_table': 'fusion_generate_case',
},
),
]
# Generated by Django 3.1.2 on 2021-04-23 03:34
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('generateCase', '0001_initial'),
]
operations = [
migrations.AlterField(
model_name='generatecase',
name='headers',
field=models.CharField(max_length=5000, verbose_name='请求头'),
),
]
# Generated by Django 3.1.2 on 2021-04-25 03:34
from django.db import migrations, models
import django.db.models.deletion
class Migration(migrations.Migration):
dependencies = [
('generateCase', '0002_auto_20210423_1134'),
]
operations = [
migrations.CreateModel(
name='GenerateCaseRunRecord',
fields=[
('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('create_time', models.DateTimeField(auto_now=True, verbose_name='运行时间')),
('name', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='generateCase.generatecasename', verbose_name='用例名称')),
],
options={
'verbose_name': '用例运行记录',
'verbose_name_plural': '用例运行记录',
'db_table': 'fusion_generate_run_record',
'ordering': ['-create_time'],
},
),
migrations.RemoveField(
model_name='generatecase',
name='runTime',
),
migrations.AlterField(
model_name='generatecase',
name='expect_code',
field=models.IntegerField(verbose_name='预期状态码'),
),
migrations.CreateModel(
name='GenerateRunStepRecord',
fields=[
('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('url', models.CharField(max_length=200, verbose_name='请求的url')),
('http_method', models.CharField(choices=[('POST', 'POST'), ('GET', 'GET'), ('PUT', 'PUT'), ('DELETE', 'DELETE')], max_length=10, verbose_name='请求方式')),
('data', models.TextField(blank=True, null=True, verbose_name='提交的数据')),
('headers', models.TextField(blank=True, null=True, verbose_name='提交的header')),
('runTime', models.DateTimeField(auto_now=True, verbose_name='运行的时间')),
('return_code', models.CharField(max_length=10, verbose_name='返回的code')),
('return_content', models.TextField(blank=True, null=True, verbose_name='返回的内容')),
('return_cookies', models.TextField(blank=True, null=True, verbose_name='返回的cookies')),
('return_headers', models.TextField(blank=True, null=True, verbose_name='返回的headers')),
('create_time', models.DateTimeField(auto_now=True, verbose_name='创建时间')),
('case', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='generateCase.generatecaserunrecord', verbose_name='用例名称')),
],
options={
'verbose_name': 'api运行记录',
'verbose_name_plural': 'api运行记录',
'db_table': 'fusion_generate_step_record',
'ordering': ['-create_time'],
},
),
]
# Generated by Django 3.1.2 on 2021-04-25 06:59
from django.db import migrations, models
import django.db.models.deletion
class Migration(migrations.Migration):
dependencies = [
('generateCase', '0003_auto_20210425_1134'),
]
operations = [
migrations.AlterModelOptions(
name='generaterunsteprecord',
options={'ordering': ['create_time'], 'verbose_name': 'api运行记录', 'verbose_name_plural': 'api运行记录'},
),
migrations.AddField(
model_name='generaterunsteprecord',
name='api',
field=models.ForeignKey(default=1, on_delete=django.db.models.deletion.CASCADE, to='generateCase.generatecase', verbose_name='api'),
preserve_default=False,
),
]
# Generated by Django 3.1.2 on 2021-04-25 08:07
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('generateCase', '0004_auto_20210425_1459'),
]
operations = [
migrations.AlterField(
model_name='generaterunsteprecord',
name='runTime',
field=models.CharField(max_length=200, verbose_name='运行的时间'),
),
]
# Generated by Django 3.1.2 on 2021-04-27 06:30
from django.db import migrations, models
import django.db.models.deletion
class Migration(migrations.Migration):
dependencies = [
('generateCase', '0005_auto_20210425_1607'),
]
operations = [
migrations.AlterField(
model_name='generaterunsteprecord',
name='api',
field=models.ForeignKey(on_delete=django.db.models.deletion.DO_NOTHING, to='generateCase.generatecase', verbose_name='api'),
),
migrations.AlterField(
model_name='generaterunsteprecord',
name='case',
field=models.ForeignKey(on_delete=django.db.models.deletion.DO_NOTHING, to='generateCase.generatecaserunrecord', verbose_name='用例名称'),
),
]
# Generated by Django 3.1.2 on 2021-04-27 06:32
from django.db import migrations, models
import django.db.models.deletion
class Migration(migrations.Migration):
dependencies = [
('generateCase', '0006_auto_20210427_1430'),
]
operations = [
migrations.AlterField(
model_name='generaterunsteprecord',
name='api',
field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, to='generateCase.generatecase', verbose_name='api'),
),
migrations.AlterField(
model_name='generaterunsteprecord',
name='case',
field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, to='generateCase.generatecaserunrecord', verbose_name='用例名称'),
),
]
# Generated by Django 3.1.2 on 2021-05-06 08:13
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('generateCase', '0007_auto_20210427_1432'),
]
operations = [
migrations.AddField(
model_name='generatecasename',
name='is_delete',
field=models.BooleanField(default=False, verbose_name='标记删除'),
),
]
# Generated by Django 3.1.2 on 2021-05-10 06:44
from django.db import migrations, models
import django.db.models.deletion
class Migration(migrations.Migration):
dependencies = [
('generateCase', '0008_generatecasename_is_delete'),
]
operations = [
migrations.AlterField(
model_name='generatecase',
name='argumentExtract',
field=models.CharField(blank=True, max_length=2000, verbose_name='参数提取'),
),
migrations.AlterField(
model_name='generatecase',
name='data',
field=models.CharField(max_length=5000, verbose_name='请求参数'),
),
migrations.AlterField(
model_name='generatecase',
name='expect_content',
field=models.CharField(blank=True, max_length=2000, verbose_name='断言内容'),
),
migrations.AlterField(
model_name='generatecase',
name='name',
field=models.ForeignKey(null=True, on_delete=django.db.models.deletion.CASCADE, related_name='step', to='generateCase.generatecasename', verbose_name='用例名称'),
),
migrations.AlterField(
model_name='generatecase',
name='path',
field=models.CharField(max_length=1000, verbose_name='请求路径'),
),
migrations.AlterField(
model_name='generatecase',
name='request_type',
field=models.CharField(choices=[('json', 'json'), ('data', 'data')], max_length=100, verbose_name='请求类型'),
),
]
# Generated by Django 3.1.2 on 2021-05-24 09:34
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('generateCase', '0009_auto_20210510_1444'),
]
operations = [
migrations.AddField(
model_name='generatecasename',
name='create_time',
field=models.DateTimeField(auto_now=True, verbose_name='创建时间'),
),
]
# Generated by Django 3.1.2 on 2022-04-07 02:12
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('generateCase', '0010_generatecasename_create_time'),
]
operations = [
migrations.AddField(
model_name='generatecase',
name='remarks',
field=models.CharField(blank=True, max_length=1000, verbose_name='备注'),
),
migrations.AddField(
model_name='generatecase',
name='sleep_time',
field=models.IntegerField(default=0, verbose_name='等待时间'),
),
migrations.AddField(
model_name='generatecasename',
name='project',
field=models.CharField(blank=True, max_length=100, verbose_name='系统'),
),
]
from django.db import models
# 请求方法枚举
HTTP_METHOD_CHOICE = (
('POST', 'POST'),
('GET', 'GET'),
('PUT', 'PUT'),
('DELETE', 'DELETE')
)
# 请求类型
REQUEST_TYPE = (
('json', 'json'),
('data', 'data')
)
class GenerateCaseName(models.Model):
"""
导入名称
"""
name = models.CharField(max_length=100, verbose_name="用例名称")
is_delete = models.BooleanField(default=False,verbose_name="标记删除")
create_time = models.DateTimeField(auto_now=True, verbose_name='创建时间')
project = models.CharField(max_length=100,blank=True,verbose_name="系统")
class Meta:
db_table = "fusion_generate_case_name"
verbose_name="导入用例名称"
verbose_name_plural=verbose_name
def __str__(self):
return "{}".format(self.name)
class GenerateCase(models.Model):
"""
导入测试用例
"""
host = models.CharField(max_length=100, verbose_name="域名")
path = models.CharField(max_length=1000, verbose_name="请求路径")
method = models.CharField(max_length=100,choices=HTTP_METHOD_CHOICE,verbose_name="请求方法")
request_type = models.CharField(max_length=100, choices=REQUEST_TYPE, verbose_name="请求类型")
data = models.CharField(max_length=5000, verbose_name="请求参数")
headers = models.CharField(max_length=5000, verbose_name="请求头")
#argumentExtract格式
# [{"name":"","origin":"","format":""},{"name":"","origin":"","format":""}]
argumentExtract = models.CharField(max_length=2000, verbose_name="参数提取",blank=True)
expect_code = models.IntegerField(verbose_name="预期状态码")
#expect_content格式
# [{"name":"","value":""},{"name":"","value":""}]
expect_content = models.CharField(max_length=2000, verbose_name="断言内容",blank=True)
create_time = models.DateTimeField(auto_now=True, verbose_name='创建时间')
name = models.ForeignKey(GenerateCaseName, on_delete=models.CASCADE, verbose_name='用例名称',null=True,related_name='step')
sleep_time=models.IntegerField(verbose_name="等待时间",default=0)
remarks = models.CharField(max_length=1000,verbose_name="备注",blank=True)
class Meta:
db_table = "fusion_generate_case"
verbose_name="导入接口用例"
verbose_name_plural=verbose_name
def __str__(self):
return "{}".format(self.path)
class GenerateCaseRunRecord(models.Model):
"""
用例运行记录
"""
name = models.ForeignKey(GenerateCaseName,on_delete=models.CASCADE,verbose_name='用例名称')
create_time = models.DateTimeField(auto_now=True,verbose_name='运行时间')
class Meta:
ordering = ['-create_time']
db_table = "fusion_generate_run_record"
verbose_name="用例运行记录"
verbose_name_plural=verbose_name
def __str__(self):
return "{}".format(self.name)
class GenerateRunStepRecord(models.Model):
"""
用例步骤记录
"""
url = models.CharField(max_length=200,verbose_name='请求的url')
http_method = models.CharField(max_length=10, verbose_name='请求方式', choices=HTTP_METHOD_CHOICE)
data = models.TextField(blank=True,null=True,verbose_name='提交的数据')
headers = models.TextField(blank=True,null=True,verbose_name='提交的header')
runTime = models.CharField(max_length=200,verbose_name='运行的时间')
return_code = models.CharField(max_length=10,verbose_name='返回的code')
return_content = models.TextField(blank=True,null=True, verbose_name='返回的内容')
return_cookies = models.TextField(blank=True,null=True, verbose_name='返回的cookies')
return_headers = models.TextField(blank=True,null=True, verbose_name='返回的headers')
case = models.ForeignKey(GenerateCaseRunRecord,on_delete=models.SET_NULL,blank=True, null=True,verbose_name='用例名称')
create_time = models.DateTimeField(auto_now=True, verbose_name='创建时间')
api = models.ForeignKey(GenerateCase,on_delete=models.SET_NULL,blank=True, null=True,verbose_name='api')
class Meta:
ordering = ['create_time']
db_table = "fusion_generate_step_record"
verbose_name="api运行记录"
verbose_name_plural=verbose_name
def __str__(self):
return "{}".format(self.url)
# -*-coding:utf-8 -*-
# __author__ = 'wuhongbin'
# Time:2021/4/23 2:29 下午
import os
import requests
from urllib import parse
import json
import re
from ast import literal_eval
from utils.dingDing import DingDing
from .models import GenerateRunStepRecord
from requests.packages.urllib3.exceptions import InsecureRequestWarning
requests.packages.urllib3.disable_warnings(InsecureRequestWarning)
from lwjTest.settings import logger
from .data_function import DataFunction
def _replace_argument(target_str,arguments):
"""
:param target_str: 原始数据
:param arguments: 需要替换的数据
:return:
"""
if type(target_str)==str:
if not arguments:
return target_str
while True:
search_result = re.search(r"{{(.+?)}}",target_str)
if not search_result:
break
argument_name = search_result.group(1)
if argument_name in arguments:
target_str = re.sub("{{"+argument_name+"}}",str(arguments[argument_name]),target_str)
else:
target_str = re.sub("{{"+argument_name+"}}",argument_name,target_str)
return target_str
elif type(target_str)==dict:
target_str = json.dumps(target_str)
if not arguments:
return target_str
while True:
search_result = re.search(r"{{(.+?)}}",target_str)
if not search_result:
break
argument_name = search_result.group(1)
if argument_name in arguments:
target_str = re.sub("{{"+argument_name+"}}",arguments[argument_name],target_str)
else:
target_str = re.sub("{{"+argument_name+"}}",argument_name,target_str)
return json.loads(target_str)
elif type(target_str)==list:
target_str = str(target_str)
if not arguments:
return target_str
while True:
search_result = re.search(r"{{(.+?)}}", target_str)
if not search_result:
break
argument_name = search_result.group(1)
if argument_name in arguments:
target_str = re.sub("{{" + argument_name + "}}", arguments[argument_name], target_str)
else:
target_str = re.sub("{{" + argument_name + "}}", argument_name, target_str)
return literal_eval(target_str)
elif type(target_str)==int:
return target_str
elif type(target_str)==bool:
return target_str
elif type(target_str)==float:
return target_str
def run_request(api,arguments=None,generateCaseRunId=None):
host = api.host
method = api.method
request_type = api.request_type
path = api.path
url = parse.urljoin(host, path)
url = _replace_argument(url,arguments)
logger.info("请求的url:{}".format(url))
#替换请求参数的变量
data = dict()
if api.data:
#data请求类型的参数格式
if request_type == "data":
request_data_list = literal_eval(api.data)
for data_dict in request_data_list:
data_key = data_dict['name']
original_data = data_dict['value']
# #处理参数需要使用自定义方法
if "___" in original_data:
FUNC_EXPR = '___(.*?){(.*?)}'
funcs = re.findall(FUNC_EXPR, original_data)
original_data = DataFunction().data_parameterization(funcs)
data_value = _replace_argument(original_data,arguments)
data[data_key] = data_value
#json请求类型的参数格式
elif request_type == "json":
data_dict=json.loads(api.data)
if type(data_dict) == dict:
for key,value in data_dict.items():
#处理参数需要使用自定义方法
if type(value)==str and "___" in value:
FUNC_EXPR = '___(.*?){(.*?)}'
funcs = re.findall(FUNC_EXPR, value)
value = DataFunction().data_parameterization(funcs)
#处理参数变量
if "{{" not in api.data:
data[key] = value
else:
data[key] = _replace_argument(value,arguments)
elif type(data_dict) == list:
data = data_dict
for list in data:
for key,value in list:
# 处理参数需要使用自定义方法
if type(value) == str and "___" in value:
FUNC_EXPR = '___(.*?){(.*?)}'
funcs = re.findall(FUNC_EXPR, value)
value = DataFunction().data_parameterization(funcs)
# 处理参数变量
if "{{" not in api.data:
data[key] = value
else:
data[key] = _replace_argument(value, arguments)
logger.info("请求参数:{}".format(data))
headers = {}
if api.headers:
headers_list = literal_eval(api.headers)
for headers_dict in headers_list:
headers_key = headers_dict['name']
headers_value = _replace_argument(headers_dict['value'], arguments)
headers[headers_key] = headers_value
logger.info("请求头:{}".format(headers))
logger.info("==============发起请求====================")
if request_type=="json":
res = requests.request(method, url, headers=headers, json=data,verify=False,allow_redirects=False)
logger.info("response:{}".format(res.text))
elif request_type=="data":
#根据Content-Type判断是否是上传文件接口
if "services/upload/order" and "api/attachment/content" not in url:
res = requests.request(method, url, headers=headers, data=data,verify=False,allow_redirects=False)
logger.info("response:{}".format(res.text))
else:
BASE_DIR = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
files = {"file": ("2.jpg", open(BASE_DIR+"/../file/2.jpg", "rb"), "image/jpeg")}
res = requests.request(method, url, headers=headers, files=files,data=data,verify=False,allow_redirects=False)
logger.info("response:{}".format(res.text))
logger.info("==============请求结束====================")
#过滤非正常格式的接口数据
if "<!DOCTYPE html>" in res.text:
return_content=""
else:
return_content=res.text
#接口响应时间
runTime = res.elapsed.total_seconds()
if runTime>20:
content="title:*******响应超时提醒********\n" \
"url:{}\n" \
"runTime:{}\n".format(url,runTime)
# DingDing().get_message(content)
# 保存运行记录
GenerateRunStepRecord.objects.create(
url = url,
http_method = method,
data = data,
headers = headers,
runTime = runTime,
return_code = res.status_code,
return_content = return_content,
return_cookies = requests.utils.dict_from_cookiejar(res.cookies),
return_headers = res.headers,
case = generateCaseRunId,
api = api
)
return res
# -*-coding:utf-8 -*-
# __author__ = 'wuhongbin'
# Time:2021/4/23 3:32 下午
import requests
import re
import os
import json
import yaml
import time
from lwjTest.settings import logger
from utils.dingDing import DingDing
from ast import literal_eval
from .models import GenerateCase,GenerateCaseName,GenerateCaseRunRecord,GenerateRunStepRecord
from .serializers import GenerateRunStepRecordSerializer
from utils.dictor import dictor
from .run_api import run_request
from apps.report.models import ReportModel
from .run_api import _replace_argument
from .data_function import DataFunction
def calculation_amount(amount_expression,global_arguments):
"""
金额计算
:param amount_expression: 断言表达式
:param global_arguments: 全局参数
:return:
"""
if isinstance(amount_expression,str):
if "sum" in amount_expression:
data_expression = amount_expression.split("=")[1]
variable_data_one = data_expression.split("+")[0]
variable_data_two = data_expression.split("+")[1]
data_one = float(_replace_argument(variable_data_one, global_arguments))
data_two = float(_replace_argument(variable_data_two, global_arguments))
logger.info("结算后的金额:{}".format(float(data_one+data_two)))
return DataFunction().get_num(data_one+data_two)
elif "reduce" in amount_expression:
data_expression = amount_expression.split("=")[1]
variable_data_one = data_expression.split("-")[0]
variable_data_two = data_expression.split("-")[1]
data_one = float(_replace_argument(variable_data_one, global_arguments))
data_two = float(_replace_argument(variable_data_two, global_arguments))
logger.info("结算后的金额:{}".format(float(data_one - data_two)))
return DataFunction().get_num(data_one-data_two)
elif "{{" in amount_expression:
return DataFunction().get_num(float(_replace_argument(amount_expression, global_arguments)))
else:
return amount_expression
else:
return amount_expression
def run_case_list(case_id_list):
"""
批量执行测试用例
:param case_id_list:
:return:
"""
case_response_list = []
for case_id in list(case_id_list):
logger.info("执行测试用例id:{}".format(case_id))
serializer_list = []
# 全局参数
global_arguments = {}
#创建测试用例运行记录
generateCaseName = GenerateCaseName.objects.get(pk=case_id)
generateCaseRunRecord = GenerateCaseRunRecord.objects.create(name=generateCaseName)
api_apis = GenerateCase.objects.filter(name_id=case_id)
for api in api_apis:
#运行api
res = run_request(api=api,arguments=global_arguments,generateCaseRunId=generateCaseRunRecord)
logger.info("cookies:--------{}".format(requests.utils.dict_from_cookiejar(res.cookies)))
#等待时间
time.sleep(api.sleep_time)
#运行API后,看下是否还有参数需要提取
if api.argumentExtract:
api_argument_list = literal_eval(api.argumentExtract)
for api_argument_dict in api_argument_list:
dictor_data = {}
# 请求头
if api_argument_dict['origin'] == 'HEAD':
dictor_data = res.headers
logger.info("headers—response:{}".format(dictor_data))
elif api_argument_dict['origin'] == 'COOKIE':
# 获取cookies返回的字典
dictor_data = requests.utils.dict_from_cookiejar(res.cookies)
logger.info("cookies-response:{}".format(dictor_data))
# 响应
elif api_argument_dict['origin'] == 'BODY':
dictor_data = res.json()
logger.info("body-response:{}".format(dictor_data))
argument_value = dictor(dictor_data,api_argument_dict['format'])
logger.info("参数提取:{}---{}".format(api_argument_dict['name'],argument_value))
#特殊处理,提取内容后,对内容做正则提取或者截取
if api_argument_dict['regular']==None or api_argument_dict['regular']=='':
pass
else:
argument_value = re.split(api_argument_dict['regular'],argument_value)[1]
global_arguments[api_argument_dict['name']] = str(argument_value)
logger.info("全局参数:{}".format(global_arguments))
#获取运行记录
generateRunStepRecord = GenerateRunStepRecord.objects.filter(case=generateCaseRunRecord)
serializer = GenerateRunStepRecordSerializer(generateRunStepRecord,many=True).data
serializer_list.append(serializer)
result_json = json.dumps(serializer, ensure_ascii=False)
result_list = json.loads(result_json)
# logger.info("result_list:{}".format(result_list))
for i in range(len(result_list)):
api_id = result_list[i].get("api")
# 用例名称
case_name = generateCaseName.name
# 请求url
url = result_list[i].get("url")
# 请求方法
method = result_list[i].get("http_method")
# 参数
data = result_list[i].get("data")
#请求头
headers = result_list[i].get("headers")
#cookie
cookie = result_list[i].get("cookie")
# 预期状态码
expect_code = GenerateCase.objects.get(pk=api_id).expect_code
# 预期结果
expect_content = GenerateCase.objects.get(pk=api_id).expect_content
# 响应状态码
return_code = int(result_list[i].get("return_code"))
# 响应内容
return_content = result_list[i].get("return_content")
#响应时间
return_time = result_list[i].get("runTime")
#api备注
api_remakes = GenerateCase.objects.get(pk=api_id).remarks
#备注,主要是查看断言失败的原因
remarks = []
assert_code=""
# 断言状态码
if expect_code == return_code:
#断言内容
logger.info("断言数据:{}".format(result_list[i].get("expect_content")))
if expect_content:
#遍历断言内容
for assert_content in literal_eval(expect_content):
actual_value = dictor(json.loads(return_content), assert_content['name'])
assert_value = assert_content['value']
assert_value = calculation_amount(assert_value,global_arguments)
#每个内容断言确认
if actual_value == assert_value:
assert_code = "pass"
else:
assert_code = "fail"
remarks.append("断言内容不一致,响应数据提取内容:{},预期内容:{},实际内容:{}".format(assert_content['name'],assert_value, actual_value))
logger.error("断言内容不一致,预期内容:{},实际内容:{}".format(assert_value, actual_value))
break
else:
assert_code = "pass"
#状态码不一致
else:
assert_code = "fail"
remarks.append("状态码不一致,预期状态码:{},实际状态码:{}".format(expect_code,return_code))
logger.error("状态码不一致,预期状态码:{},实际状态码:{}".format(expect_code,return_code))
if assert_code=="fail":
content="url:{}\n" \
"method:{}\n" \
"data:{}\n" \
"expect_code:{}\n" \
"expect_content:{}\n" \
"return_code:{}\n" \
"assert_code:{}\n" \
"return_time:{}\n" \
"return_content:{}\n" \
"remarks:{}".format(url,method,data,expect_code,expect_content,return_code,assert_code,return_time,return_content,remarks)
# DingDing().get_message(content)
case_response_list.insert(i, [case_name, url, method, data, expect_code,expect_content,headers,cookie,return_code,return_time, return_content,assert_code,remarks,api_remakes])
with open("utils/response.txt", "w") as f:
f.write(json.dumps(case_response_list, ensure_ascii=False))
os.system('python3 utils/testGenerateManySuite.py')
curpath = os.path.dirname(os.path.realpath(__file__)) # 获取文件当前路径
yamlpath = os.path.join(curpath, "../../utils/case.yaml") # 获取yaml文件地址
data = open(yamlpath, 'r')
report_data = yaml.load(data.read(), Loader=yaml.Loader)
ReportModel.objects.create(
project_name=report_data['project_name'],
project_host=report_data['project_host'],
case_type=report_data['case_type'],
case_all=report_data['case_all'],
case_pass=report_data['case_pass'],
case_fail=report_data['case_fail'],
start_time=report_data['start_time'],
run_time=report_data['run_time'],
report_details=report_data['report_details']
)
return report_data['report_details']
def run_case_task(case_id):
"""
定时任务调运行用例
:param case_id:
:return:
"""
case_list = literal_eval(case_id)
run_case_list(case_list)
# -*-coding:utf-8 -*-
# __author__ = 'wuhongbin'
# Time:2021/4/23 9:56 上午
from rest_framework.serializers import ModelSerializer
from .models import GenerateCase,GenerateCaseRunRecord,GenerateRunStepRecord,GenerateCaseName
from rest_framework.serializers import ValidationError
class GenerateCaseSerializer(ModelSerializer):
"""
导入测试用例
"""
class Meta:
model = GenerateCase
fields = "__all__"
class GenerateCaseRunRecordSerializer(ModelSerializer):
"""
用例运行记录
"""
class Meta:
model = GenerateCaseRunRecord
fields = "__all__"
class GenerateRunStepRecordSerializer(ModelSerializer):
"""
用例步骤记录
"""
class Meta:
model = GenerateRunStepRecord
fields = "__all__"
class GenerateCaseNameSerializer(ModelSerializer):
"""
用例
"""
step = GenerateCaseSerializer(many=True)
class Meta:
model = GenerateCaseName
fields = ['id','name','step']
from django.test import TestCase
# Create your tests here.
\ No newline at end of file
# -*-coding:utf-8 -*-
# __author__ = 'wuhongbin'
# Time:2021/2/20 2:02 下午
from django.urls import path,include,re_path
from . import views
from rest_framework.routers import DefaultRouter
rouer = DefaultRouter()
#用例步骤
rouer.register('step',views.GenerateCaseRunRecordViewSet,basename='step')
urlpatterns = [
#导入测试用例
path('case', views.GenerateCaseAPIView.as_view()),
#执行测试用例
path('run',views.CaseRunApiView.as_view()),
path('run/<pk>',views.CaseRunApiView.as_view()),
#清洗headers
path('clean',views.CaseHeadersClean.as_view()),
#汇总用例
path('summary',views.SummaryCaseApiView.as_view())
]+rouer.urls
This diff is collapsed.
default_app_config = 'apps.report.apps.ReportConfig'
from django.contrib import admin
from .models import *
@admin.register(ReportModel)
class ProjectAdmin(admin.ModelAdmin):
"""测试报告"""
list_display = ['project_name','project_host','case_type','case_all','case_pass','case_fail','start_time']
from django.apps import AppConfig
class ReportConfig(AppConfig):
name = 'apps.report'
verbose_name ='测试报告'
# Generated by Django 3.1.2 on 2021-02-19 09:06
from django.db import migrations, models
class Migration(migrations.Migration):
initial = True
dependencies = [
]
operations = [
migrations.CreateModel(
name='Meail',
fields=[
('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('recipient', models.CharField(max_length=100, verbose_name='收件人邮箱')),
],
options={
'verbose_name': '报告邮箱',
'verbose_name_plural': '报告邮箱',
'db_table': 'fusion_meail',
},
),
migrations.CreateModel(
name='ReportModel',
fields=[
('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('project_name', models.CharField(max_length=100, verbose_name='项目名称')),
('project_host', models.CharField(blank=True, choices=[['开发环境', '开发环境'], ['测试环境', '测试环境'], ['线上环境', '线上环境']], default=None, max_length=100, null=True, verbose_name='用例执行环境')),
('case_type', models.CharField(choices=[['ui', 'ui'], ['接口', '接口']], default=1, max_length=100, verbose_name='用例类型')),
('case_all', models.IntegerField(verbose_name='用例总数')),
('case_pass', models.IntegerField(verbose_name='用例成功次数')),
('case_fail', models.IntegerField(verbose_name='用例失败次数')),
('start_time', models.CharField(max_length=100, verbose_name='开始时间')),
('run_time', models.CharField(max_length=100, verbose_name='运行时间')),
('report_details', models.CharField(max_length=100, verbose_name='报告详情')),
('is_delete', models.BooleanField(default=False, verbose_name='是否删除')),
('create_time', models.DateTimeField(auto_now_add=True, verbose_name='创建时间')),
],
options={
'verbose_name': '报告表',
'verbose_name_plural': '报告表',
'db_table': 'fusion_report',
'ordering': ['-create_time'],
},
),
]
# Generated by Django 3.1.2 on 2021-02-25 06:19
from django.db import migrations
class Migration(migrations.Migration):
dependencies = [
('report', '0001_initial'),
]
operations = [
migrations.DeleteModel(
name='Meail',
),
]
# Generated by Django 3.1.2 on 2022-04-26 08:53
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('report', '0002_delete_meail'),
]
operations = [
migrations.AlterField(
model_name='reportmodel',
name='report_details',
field=models.CharField(max_length=300, verbose_name='报告详情'),
),
]
# Generated by Django 3.1.2 on 2022-05-07 05:55
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('report', '0003_auto_20220426_1653'),
]
operations = [
migrations.CreateModel(
name='Email',
fields=[
('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('email', models.CharField(max_length=100, verbose_name='邮箱地址')),
('name', models.CharField(max_length=100, verbose_name='姓名')),
('status', models.BooleanField(default=True, verbose_name='是否禁用')),
],
options={
'verbose_name': '邮箱表',
'verbose_name_plural': '邮箱表',
'db_table': 'fusion_email',
},
),
]
# Generated by Django 3.1.2 on 2022-05-07 06:20
from django.db import migrations
class Migration(migrations.Migration):
dependencies = [
('report', '0004_email'),
]
operations = [
migrations.RenameModel(
old_name='Email',
new_name='EmailModel',
),
]
from django.db import models
# Create your models here.
from django.db import models
class ReportModel(models.Model):
"""
测试报告
"""
PROJECT_URL_CHOICES = [
["开发环境","开发环境"],
["测试环境","测试环境"],
["线上环境","线上环境"]
]
PROJECT_TYPE = [
["ui","ui"],
["接口","接口"]
]
project_name = models.CharField(max_length=100,verbose_name="项目名称")
project_host = models.CharField(choices=PROJECT_URL_CHOICES,max_length=100,default=None,null=True,blank=True,verbose_name="用例执行环境")
case_type= models.CharField(choices=PROJECT_TYPE,max_length=100,default=1,verbose_name="用例类型")
case_all=models.IntegerField(verbose_name="用例总数")
case_pass=models.IntegerField(verbose_name="用例成功次数")
case_fail=models.IntegerField(verbose_name="用例失败次数")
start_time=models.CharField(max_length=100,verbose_name="开始时间")
run_time = models.CharField(max_length=100,verbose_name="运行时间")
report_details = models.CharField(max_length=300,verbose_name="报告详情")
is_delete= models.BooleanField(default=False,verbose_name="是否删除")
create_time= models.DateTimeField(auto_now_add=True,verbose_name="创建时间")
class Meta:
ordering = ['-create_time']
db_table = "fusion_report"
verbose_name = "报告表"
verbose_name_plural = verbose_name
def __str__(self):
return self.project_name
class EmailModel(models.Model):
email = models.CharField(max_length=100,verbose_name="邮箱地址")
name = models.CharField(max_length=100,verbose_name="姓名")
status = models.BooleanField(default=True,verbose_name="是否禁用")
class Meta:
db_table = "fusion_email"
verbose_name = "邮箱表"
verbose_name_plural = verbose_name
def __str__(self):
return self.email
# -*-coding:utf-8 -*-
# __author__ = 'wuhongbin'
# Time:2021/1/25 11:14 上午
import re
from rest_framework.serializers import ModelSerializer
from .models import ReportModel,EmailModel
class ReportModelSerializer(ModelSerializer):
class Meta:
model = ReportModel
fields = ['id','project_name','project_host','case_type','case_all','case_pass','case_fail','report_details','create_time']
class EmailSerializer(ModelSerializer):
class Meta:
model = EmailModel
fields = "__all__"
from django.test import TestCase
# Create your tests here.
# -*-coding:utf-8 -*-
# __author__ = 'wuhongbin'
# Time:2021/2/25 9:55 上午
from django.urls import path,include,re_path
from . import views
from rest_framework.routers import DefaultRouter
rouer = DefaultRouter()
#项目
rouer.register('email',views.EmailViewSet)
urlpatterns = [
#报告列表
re_path('list/', views.ReportVIew.as_view()),
path('<int:id>', views.ReportVIew.as_view()),
path('send/meail/<name>',views.mailReport),
#报告详情
path('<name>',views.reportDetails),
]+rouer.urls
\ No newline at end of file
This diff is collapsed.
default_app_config = 'apps.system.apps.SystemConfig'
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment