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。


↙↙↙阅读原文可查看相关链接,并与作者交流