接口测试 开源~自研接口测试平台 Django2+Bootstrap3+Unittest

letme · August 17, 2018 · Last by pootmax replied at May 23, 2019 · 9175 hits

python3.6.4 Django2.0 框架

文档更新

第一篇:https://testerhome.com/topics/13269
第二篇:https://testerhome.com/topics/14801
第三篇:https://testerhome.com/topics/15352

版本更新

V1.8 引入mongo对于返回结果值存储,实现接口依赖功能。
V1.7 优化断言模块,采用json方法多层级遍历,增加断言比较方法
V 1.6 修复环境配置取消必须绑定端口号,接口测试取消必须连接数据库,优化定时模块
V 1.5 实现可选择邮箱发送
V 1.4 实现日志模块和报告数据分析模块
V 1.3 引入任务概念,实现定时模块(双环境)的控制
V 1.2 定义生成脚本规则,实现自动生成脚本,动态引入sql,增加unittest的失败重跑机制
V 1.1 实现配置化管理,环境配置,数据库配置,邮箱配置
V 1.0 实现用例步骤维护,实现3种http调用,引入django和unittest

数据库结构

登录模块

项目和模块页面


用例

用例中的用例名需要设置成英文,api不需要带上http://。
如果是状态为false的用例不支持任务添加,支持用例新增,修改,以及批量升级

步骤

一个用例对应多个步骤,步骤名需要使用中文,目前可以使用前3种方式请求。
信息头、参数、断言都可以使用变量,变量格式为${}
支持步骤新增,修改,以及批量升级

断言

断言例如可以采用['result']['data'][0]['id']这样的方式作为对返回值的获取,可采用的方法也有多种
当按钮关闭时表示不使用断言

接口依赖

一个步骤可以去依赖另一个步骤,可以多层级依赖,也可以选择依赖多个或被多个依赖,需要配置依赖那个步骤,依赖步骤返回值的路径(跟断言取值方法一致),以及变量名
当按钮关闭时表示不使用接口依赖

配置页面



生成脚本

在用例页面勾选对应的用例去生成任务,任务名设置为英文,对应的执行结果task目录下生成以任务名命名的文件夹,文件夹下testcase目录以及report目录,前者存放生成的脚本,后者生成日志和邮件报告

定时任务

如果选择单次执行,那么关闭定时任务就可以。定时任务input框可以点击进入,配置方法采用crontab。状态是配置了定时时间之后才去判断状态的,false表示不启动。

报告分析

5次内执行结果,失败数,断言失败数,成功数等等,也会将反馈和今日错误反馈给出。

这里有个地方需要配置,我默认用的message,如果你接口第一层返回值定义的是其他信息时,需要修改make_testcase.py的两个地方

安装步骤

依赖库安装:
Django 2.0.2
django-bootstrap3
pymysql,pymssql,DBUtils
Requests
bs4
apscheduler
pymongo
两个环境的前提,修改uniitest源码:
1.新增unittest的case.py文件中TestCase类加入一个方法

#自主添加的方法
def chooseAssertWay(self,response,assertway,assert_response):
if assertway=="assertEqual":
self.assertEqual(response,assert_response)
elif assertway=="assertNotEqual":
self.assertNotEqual(response,assert_response)
elif assertway=="assertRegexpMatches":
self.assertRegexpMatches(response,assert_response)
elif assertway=="assertNotRegexpMatches":
self.assertNotRegexpMatches(response,assert_response)
elif assertway=="assertGreater":
self.assertGreater(response,assert_response)
elif assertway=="assertGreaterEqual":
self.assertGreaterEqual(response,assert_response)
elif assertway=="assertLess":
self.assertLess(response,assert_response)
elif assertway=="assertLessEqual":
self.assertLessEqual(response,assert_response)
elif assertway=="assertIn":
self.assertIn(response,assert_response)
elif assertway=="assertNotIn":
self.assertNotIn(response,assert_response)

2.更改unittest的loader.py文件中TestLoader类一个方法discover

def discover(self, start_dir,case_names_weights, pattern='test*.py', top_level_dir=None):
"""Find and return all test modules from the specified start
directory, recursing into subdirectories to find them and return all
tests found within them. Only test files that match the pattern will
be loaded. (Using shell style pattern matching.)

All test modules must be importable from the top level of the project.
If the start directory is not the top level directory then the top
level directory must be specified separately.

If a test package name (directory with '__init__.py') matches the
pattern then the package will be checked for a 'load_tests' function. If
this exists then it will be called with (loader, tests, pattern) unless
the package has already had load_tests called from the same discovery
invocation, in which case the package module object is not scanned for
tests - this ensures that when a package uses discover to further
discover child tests that infinite recursion does not happen.

If load_tests exists then discovery does *not* recurse into the package,
load_tests is responsible for loading all tests in the package.

The pattern is deliberately not stored as a loader attribute so that
packages can continue discovery themselves. top_level_dir is stored so
load_tests does not need to pass this argument in to loader.discover().

Paths are sorted before being imported to ensure reproducible execution
order even on filesystems with non-alphabetical ordering like ext3/4.
"""

set_implicit_top = False
if top_level_dir is None and self._top_level_dir is not None:
# make top_level_dir optional if called from load_tests in a package
top_level_dir = self._top_level_dir
elif top_level_dir is None:
set_implicit_top = True
top_level_dir = start_dir

top_level_dir = os.path.abspath(top_level_dir)

if not top_level_dir in sys.path:
# all test modules must be importable from the top level directory
# should we *unconditionally* put the start directory in first
# in sys.path to minimise likelihood of conflicts between installed
# modules and development versions?
sys.path.insert(0, top_level_dir)
self._top_level_dir = top_level_dir

is_not_importable = False
is_namespace = False
tests = []
if os.path.isdir(os.path.abspath(start_dir)):
start_dir = os.path.abspath(start_dir)
if start_dir != top_level_dir:
is_not_importable = not os.path.isfile(os.path.join(start_dir, '__init__.py'))
else:
# support for discovery from dotted module names
try:
__import__(start_dir)
except ImportError:
is_not_importable = True
else:
the_module = sys.modules[start_dir]
top_part = start_dir.split('.')[0]
try:
start_dir = os.path.abspath(
os.path.dirname((the_module.__file__)))
except AttributeError:
# look for namespace packages
try:
spec = the_module.__spec__
except AttributeError:
spec = None

if spec and spec.loader is None:
if spec.submodule_search_locations is not None:
is_namespace = True

for path in the_module.__path__:
if (not set_implicit_top and
not path.startswith(top_level_dir)):
continue
self._top_level_dir = \
(path.split(the_module.__name__
.replace(".", os.path.sep))[0])
tests.extend(self._find_tests(path,
pattern,
namespace=True))
elif the_module.__name__ in sys.builtin_module_names:
# builtin module
raise TypeError('Can not use builtin modules '
'as dotted module names') from None
else:
raise TypeError(
'don\'t know how to discover from {!r}'
.format(the_module)) from None

if set_implicit_top:
if not is_namespace:
self._top_level_dir = \
self._get_directory_containing_module(top_part)
sys.path.remove(top_level_dir)
else:
sys.path.remove(top_level_dir)

if is_not_importable:
raise ImportError('Start directory is not importable: %r' % start_dir)

if not is_namespace:
tests = list(self._find_tests(start_dir, pattern))
#print (tests)
#正则提取用例名,排序
regex=re.compile(r'testcase\.(.+?)\.')
for i in range(0, len(tests) - 1):
for j in range(2, len(tests) - 1 - i):
try:
if case_names_weights[regex.findall(str(tests[j]))[0]] > case_names_weights[regex.findall(str(tests[j+1]))[0]]:
c = tests[j]
tests[j] = tests[j + 1]
tests[j + 1] = c
except:
# 方法为空时不变
pass
return self.suiteClass(tests)

windows部署:
1.根据testhome安装python3.6和对应的python库(还需要添加HTMLTestRunner.py)
2.选择一个mysql数据库新建一个request数据库作为测试库,在django的setting.py文件的86行配置数据库的信息(ip,端口,数据库名称,用户名,密码)
3.选择一个mongo数据库作为测试库,在django的setting.py文件的103行配置数据库的信息(ip,端口,集合,一张表)
4.进入到项目根目录,数据库迁移:python manage.py makemigrations在request应用下的migrations目录下创建了一个 0001_initial.py 文件,执行python manage.py migrate,执行完成库表生成
5.创建第一个用户python manage.py createsuperuser
6.django的setting.py文件的28行,因为是本地启动保持ALLOWED_HOSTS = []就好了
7.配置编译器启动方式,选择django server启动,HOST填写127.0.0.1,port填写8000,运行
8.访问127.0.0.1:8000,能访问到就ok了

linux部署
1.根据testhome安装python3.6和对应的python库(还需要添加HTMLTestRunner.py)
2.把源代码放到linux下(我创建了pj目录,项目放在/home/pj下)
3.选择一个mysql数据库新建一个request数据库作为测试库,在django的setting.py文件的86行配置数据库的信息(ip,端口,数据库名称,用户名,密码)
4.选择一个mongo数据库作为测试库,在django的setting.py文件的103行配置数据库的信息(ip,端口,集合,一张表)
5.进入到项目根目录,数据库迁移:python manage.py makemigrations在request应用下的migrations目录下创建了一个 0001_initial.py 文件,执行python manage.py migrate,执行完成库表生成
6.创建第一个用户python manage.py createsuperuser,之后的用户登录http://192.168.100.158:8000/admin/去创建用户组和用户以及分配权限
7.在pj目录下创建logs目录,下面创建request.log文件存放项目日志文件
8.在django的setting.py文件的28行,添加自己linux的ip(我的是ALLOWED_HOSTS = ['192.168.100.158'])
9.把启动shell和关闭shell放在根目录下(requestnew),sh start.sh,项目就启动了;项目关闭则执行sh shutdown.sh
10.如果目录结构想要有所调整或者启动端口(默认8000)有所调整,需要修改启动和关闭文件
11.其他机子能访问到(配置的ip:8000)就成功了。

老用户部署会涉及到数据库迁移的问题,建议百度学习,将老项目requestnew\request\migrations除了init.py都删除先数据库迁移,然后将这个新生成的0001_initial.py文件放到新工程下的同目录下的地方在对新工程做数据库迁移。

github代码暂时停更,遇到了上传不了代码的问题。源代码放置群655981739。

如果觉得我的文章对您有用,请随意打赏。您的支持将鼓励我继续创作!
共收到 24 条回复 时间 点赞
1Floor has been deleted

另外,如果有演示站就好了。
对python不算太熟,而且感觉部署起来有点麻烦

感谢分享@@

—— 来自TesterHome官方 安卓客户端

letme #4 · August 20, 2018 作者
徐汪成 回复

java我也不太熟,思想是可以一起交流下的。部署方面按照一个django项目的部署,是会有点麻烦。

😶 😶 😶 😶 😶 加油!

你好,麻烦问一下楼主,接口自动化自己写脚本和直接使用类似于jmeter工具进行接口测试有什么区别?是不是使用现成的工具会性价比高一点(因为自己写脚本还可能存在很多的bug)?

letme #7 · August 20, 2018 作者
ty 回复

其实jmeter这些工具很强大,和自己写脚本最大的区别,我觉得在jmeter是已经写好方法了,你只要把提供数据 然后按规则串接口,自己写的更加灵活,你可以按自己的想法定义规则。Python中的requests包提供的http也确实够简单的,而且你可以很方便的集成各式各样的库,去丰富自己的功能。

其实,在实际项目当中,我常纠结写脚本,比如写自动化,项目一个接一个,你可能还没写几个,需求可能都变了,所以一直想找一个快捷的,能实际解决问题,大公司有自己的框架,也有一群人在维护,而小公司往往只有一个人,有点分身乏术的感觉,花时间到底对工作的帮助大不大,是否实际,我常纠结于此,以上仅个人感受,总的来说感谢您的回答~

总体来说,要多学习,楼主很棒,向楼主学习

ty 回复

jmeter这种工具虽然给你界面什么的都写好了,但是测试平台自己会编写的必要性,就是既可以了解业务,实现业务,也可以把自己写的一些源代码无缝接入到有前端后台完全自己写的工具上,这样底层的实现原理可能会更深入,更方便你思考实现一个东西是怎么样的,从而进一步引发你对测试的思考

回复

写平台对个人能力提升很多,但是实际上其实90%的开发出来的平台还没jmeter和soapUi好用呢。且不说BUG太多

@letme 新版本麻烦贴个源码地址 好吗? 我想部署试试

letme #13 · August 20, 2018 作者
ty 回复

我其实都是一个人写的,而且我毕业才1年,一个人能干的事很多的,只要你愿意干,实在干不了换环境。差距都是在不经意间拉开的。

letme #14 · August 20, 2018 作者
徐汪成 回复

源代码放置群655981739,源代码放群了。最近项目多来不及搞新的地址,github问题我也解决不了。

letme 回复

放码云啊。
码云多好

16Floor has been deleted

你直接修改下帖子,把这个内容加进去更新就行了

在添加headers的时候,为什么不能一起copy过去,还要单个键值单个键值去copy呢?这样添加一个用例光copy header就要copy很多次啊

letme [Topic was deleted] 中提及了此贴 14 Sep 09:45
letme #20 · September 14, 2018 作者
YCBDM 回复

copy的话是可以加个copy步骤的方法,其实对于搭建者来说数据库是可以操作的,我想没有比复制数据库更简单的方法了吧。目前迭代中,以重要的问题先解决,譬如说单次执行的报告和日志在web端展示,jmeter和postman的相关脚本导入等等。

问问如何加入htmltestrunner

letme #22 · October 29, 2018 作者
大大国 回复

新版不用加了 ,报告用ExtentHTMLTestRunner这个, 集成在源代码里。

在问问您,我们用哪篇文章的部署这个最新的系统?
linux部署
1.根据testhome安装python3.6和对应的python库(还需要添加HTMLTestRunner.py)
2.把源代码放到linux下(我创建了pj目录,项目放在/home/pj下)
3.选择一个mysql数据库新建一个request数据库作为测试库,在django的setting.py文件的86行配置数据库的信息(ip,端口,数据库名称,用户名,密码)
4.选择一个mongo数据库作为测试库,在django的setting.py文件的103行配置数据库的信息(ip,端口,集合,一张表)
5.进入到项目根目录,数据库迁移:python manage.py makemigrations在request应用下的migrations目录下创建了一个 0001_initial.py 文件,执行python manage.py migrate,执行完成库表生成
6.创建第一个用户python manage.py createsuperuser,之后的用户登录http://192.168.100.158:8000/admin/去创建用户组和用户以及分配权限
7.在pj目录下创建logs目录,下面创建request.log文件存放项目日志文件
8.在django的setting.py文件的28行,添加自己linux的ip(我的是ALLOWED_HOSTS = ['192.168.100.158'])
9.把启动shell和关闭shell放在根目录下(requestnew),sh start.sh,项目就启动了;项目关闭则执行sh shutdown.sh
10.如果目录结构想要有所调整或者启动端口(默认8000)有所调整,需要修改启动和关闭文件
11.其他机子能访问到(配置的ip:8000)就成功了。

letme #24 · October 30, 2018 作者
大大国 回复

这篇就可以了,我只是为了兼容老版本,所以没去掉有些部署步骤

厉害,你这一个人要做多久呢

letme #26 · December 14, 2018 作者

git上更新对外2.1.3最新版本,但是在公司内的话框架功能已经大改,bug修复了很多,希望春节前能把新框架推出来
https://github.com/happyletme/requestnew 希望大家能帮忙star一下,框架疑问的话可以加群:655981739

letme 自研接口测试平台 requestnew 中提及了此贴 15 Feb 13:45
pootmax · #28 · May 23, 2019
Author only
需要 Sign In 后方可回复, 如果你还没有账号请点击这里 Sign Up