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

letme · 2018年08月17日 · 最后由 李文良 回复于 2020年08月17日 · 4852 次阅读

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。

如果觉得我的文章对您有用,请随意打赏。您的支持将鼓励我继续创作!
共收到 25 条回复 时间 点赞
27楼 已删除

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

感谢分享 @@

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

letme #26 · 2018年08月20日 Author
徐汪成 回复

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

😶 😶 😶 😶 😶 加油!

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

ty 回复

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

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

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

ty 回复

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

回复

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

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

letme #13 · 2018年08月20日 Author
ty 回复

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

letme #14 · 2018年08月20日 Author
徐汪成 回复

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

letme 回复

放码云啊。
码云多好

16楼 已删除

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

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

letme [该话题已被删除] 中提及了此贴 09月14日 09:45
letme #10 · 2018年09月14日 Author
YCBDM 回复

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

问问如何加入 htmltestrunner

letme #22 · 2018年10月29日 Author
大大国 回复

新版不用加了 ,报告用 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 · 2018年10月30日 Author
大大国 回复

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

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

letme #26 · 2018年12月14日 Author

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

letme 自研接口测试平台 requestnew 中提及了此贴 02月15日 13:45
仅楼主可见

平台的意义在哪里?很多人都不愿意用的!

需要 登录 后方可回复, 如果你还没有账号请点击这里 注册