自动化工具 pytest 内核测试平台落地初体验

dongfanger · 2021年02月02日 · 最后由 ssx 回复于 2021年03月09日 · 4072 次阅读
本帖已被设为精华帖!

测试平台,有人说它鸡肋,有人说它有用,有人说它轮子,众说纷纭,不如从自身出发,考虑是否要做测试平台:

  • 第 1 阶段,用 Python+requests 写接口自动化。
  • 第 2 阶段,选择 unitttest 或 pytest,更熟悉 pytest 选了 pytest。
  • 第 3 阶段,快速搭建 pytest 项目脚手架,封装 tep 测试工具。
  • 第 4 阶段,通过 Git 管理测试脚本,多分支合并代码。
  • 第 5 阶段,去除本地环境同步麻烦,方便团队共享脚本。

需要有个测试平台。

使用篇

环境变量

环境变量是字符串键值对,全局作用域。比如不同环境不同域名:

使用:env_vars.name

fixtures

fixtures 即 pytest 的 fixture,可以添加自定义函数,供测试用例使用。比如封装登录接口返回 token:

tep.fixture提供了url fixture,自动拼接环境变量env_vars.domain + uri

测试用例

在前端网页写代码,1 条用例对应 1 个 pytest 的test_name.py文件。比如调用login fixture 登录:

本地编写

PyCharm 写代码体验更好,正确姿势是从平台下载包含环境变量和 fixtures 等项目结构代码,本地编写用例,调试,跑通后,粘贴到平台上共享和维护:

本地和平台环境一致,省去前期搭建,关注tests用例。

扩展能力

用例是 Python 代码,理论上所有 Python 能写出来的,平台都能支持,比如 HTTP、WebSocket、Protobuf 等协议。

原理篇

pytest 内核

  1. vue2-ace-editor作为前端代码编辑组件。
  2. 前端把代码通过 HTTP 请求传给后端。
  3. 后端把代码存入 MySQL 数据库。
  4. 运行用例,从数据库取出代码,生成 pytest 文件。
  5. Shell 命令调用pytest -s test_name.py,执行测试。
  6. 后端把运行结果日志返给前端展示。

之所以要折腾数据库,是因为每次部署后 docker 容器里面的文件会被清掉,只能动态生成。

tep 脚手架

测试平台功能是从 tep 项目脚手架中抽取出来的:

  • fixture_env_vars.py做成了环境变量功能。
  • fixture_login.py等做成了fixtures功能。
  • tests做成了测试用例功能。

运行用例

整体流程如下:

tep startproject project_name

运行用例时,判断项目目录是否存在,如果不存在就调用tep startproject project_name创建项目脚手架。

更新 conf.yaml 中 env

把前端传的当前运行环境更新到conf.yaml文件中:

env: qa

动态生成或更新 fixture_env_vars.py 文件

根据环境变量功能模块的数据,动态生成fixture_env_vars.py文件:

#!/usr/bin/python
# encoding=utf-8

from tep.dao import mysql_engine
from tep.fixture import *


@pytest.fixture(scope="session")
def env_vars(config):
    class Clazz(TepVars):
        env = config["env"]

        """Variables define start"""
        # Environment and variables
        mapping = {
            "qa": {
                "domain": "https://qa.com",
            },
            "release": {
                "domain": "https://release.com",
            }
            # Add your environment and variables
        }
        # Define properties for auto display
        domain = mapping[env]["domain"]
        """Variables define end"""

    return Clazz()

动态生成或更新 fixtures 目录下所有文件

根据fixtures功能模块的数据,动态生成fixture_login.py等所有文件:

from tep.client import request
from tep.fixture import *


def _jwt_headers(token):
    return {"Content-Type": "application/json", "authorization": f"Bearer {token}"}


@pytest.fixture(scope="session")
def login(url):
    # Code your login
    logger.info("Administrator login")
    response = request(
        "post",
        url=url("/api/users/login"),
        headers={"Content-Type": "application/json"},
        json={
            "username": "admin",
            "password": "123456",
        }
    )
    assert response.status_code < 400
    response_token = jmespath.search("token", response.json())

    class Clazz:
        token = response_token
        jwt_headers = _jwt_headers(response_token)

    return Clazz

conftest.py 会自动查找后import,tests 用例直接使用。

动态生成或更新 tests 某个 test_文件

从数据库拿到用例代码,动态生成test_文件。

Shell 执行 pytest 命令

从上一步拿到case_path,调用pytest -s case_path执行测试。

计划后续添加 suite 和 marker 两种批量执行用例方式。

小结

本文介绍了我第一次做的测试平台的使用和原理,技术栈为 Vue+Django+Django REST Framework+JWT+MySQL+pytest+Git+BitBucket+Drone+Nginx+Docker+K8S,已在公司落地,还未大规模产出,由于服务端有较多磁盘 IO 读写,大量使用后不知道性能如何,目前来看问题不大,需要持续观察和优化。测试平台底层是pytest,用到了tep,那就叫teprunner

参考资料:

https://github.com/dongfanger/tep

共收到 25 条回复 时间 点赞
恒温 将本帖设为了精华贴 02月03日 00:39

Shell 命令调用 pytest -s test_name.py,执行测试。
这一步有点奇怪哈,你这里都用到了 K8S,那直接做一个包含 pytest 的镜像,然后动态生成 pod,代码挂载或者 git clone 方式应该会比较舒服吧。然后 drone 是一个 gitops 的 CI 工具,在这里也没体现出用途。 感觉作者在文中少说了点什么

要适用大规模运行的场景 ,用例执行 就得分离出去,我们做了一个跟 你这个类似的平台,执行这块可以优化下,我们是将执行分离给专门的 runner pod(statefulset 类型),通过 pytest-xdist 中的 execnet 这个库来做热更和代码的远程执行,然后再走 allure-service-api 来生成报告。

既然都用到 pycharm 写 case 了,为什么不通过 git 来管理代码呢? 直接复制粘贴,一来不能保障都复制对了(多文件修改的情况),二来也可能把别人写的 case 覆盖了

大佬开源吗,想学习下

韩将 回复

是的,部署没有说,前后端代码是放在 BitBucket 上面的,Drone 是 CI 工具,会轮询 BitBucket 的 Pull Request,自动拉取代码打包成 docker 镜像,通过运维平台配置域名和 Nginx 路由后,部署 image 到 pod,这一套是直接用的公司流程。K8S 这块确实可以优化下。

好的,我试试,感谢建议,哈哈。

其实现在就是用 Git 管理的,走的 Pull Request,审核后再合并到主干分支,要做成平台是因为每个人的本地环境同步会稍微有点麻烦,开发也有跑用例造数据需求,平台能忽略这些问题,上手就用。还有一个重要原因是公司需要,领导需要,就找了这个折中方案。1 条用例是放在 1 个 test_name.py 文件来写的,就是为了避免多个文件依赖混乱的情况,这一点借鉴了 httprunner 一个 yaml 文件一条用例的做法。如果要改别人的用例,可以先复制用例再修改用例,避免影响别人使用。

路小圣 回复

开源估计要等到明年了,哈哈,到时候应该会写个系列文章来从头做个偏学习的版本。

看起来挺好的,先 Mark

期待早点开源

楼主,我有个 pytest 的问题想请教下。接口数量过多的时候,执行时间过长,于是考虑并发执行。pytest 有个插件是 pytest-xdist 可以支持并发。
目前我们接口自动化是通过 pytest + excle 来实现,每一个 excle 的 sheet 页对应一个功能模块的接口用例,每个 sheet 页对应到一个 .py 文件,每个 .py 文件只有 1 个测试用例,这个用例做了参数化,数据来自于 excle 的 sheet 页。
通过 pytest-xdist 这个插件的--dist loadfile 参数保证每个.py 文件运行在相同的 worker 上,但是每个测试用例经过了参数化形成了多个子用例,这个多个子用例之间的顺序怎么去保证呢?因为我们的用例有依赖关系,必须从上往下执行,如果顺序乱了,接口测试就会出现很多错误。

退之 回复

分布式执行用例的设计原则:
用例之间是独立的,用例之间没有依赖关系,用例可以完全独立运行。【独立运行】
用例执行没有顺序,随机顺序都能正常执行。【随机执行】
每个用例都能重复运行,运行结果不会影响其他用例。【不影响其他用例】

先 M,跟随学习

学习了😀

退之 回复

看了下你的描述 跟我现在的框架设计很相似 我也是用 excel 作为数据源 并且场景测试套件中用例存在依赖 不过框架用例设计上我是都是基于一个模板测试类实现的.所有的用例都在同一个测试类上面参数化然后通过组别进行分发判断.
关于 xdist 分发我写了一个插件 https://pypi.org/project/pytest-custom-scheduling/
他可以基于参数化的 ids 进行分组并发 并且支持哪怕用的是同一个模板类但是在 allure 上面依旧按照分组的名字作为类名和用例名
另外关于 xdist 分发的执行顺序问题,如果你使用的是参数化 那么执行顺序一定按照你参数化顺序执行的


fungaegis 回复

昨天回复你的时候突然灵光一闪 写了一个新的插件
https://pypi.org/project/pytest-custom-nodeid/
这款插件直接通过自定义 name 和 nodeid 从而满足 xdist 的 loadscope 级别负载策略
这样就不存在上一个插件通过修改 xdist 的负载策略实现分组的弊病:分组号外显的情况

《学习版 pytest 内核测试平台开发万字长文入门篇》已发布,访问地址:
https://dongfanger.gitee.io/blog/teprunner/002-pytest 内核测试平台开发万字长文入门篇.html学习版
一共有 103 张截图,搬运有点麻烦,等有时间再同步到社区来。

dongfanger 回复

《学习版 pytest 内核测试平台开发万字长文入门篇》已发布,访问地址:
https://dongfanger.gitee.io/blog/chapters/teprunner.html
一共有 103 张截图,搬运有点麻烦,等有时间再同步到社区来。
源码前端 https://github.com/dongfanger/teprunner-frontend
源码后端 https://github.com/dongfanger/teprunner-backend

我有一个疑问:用这个的话,那么些用例是不是还是要写代码?如果还是要写代码为啥还用这个?原生的 pycharm+git 去写会不会更加简单一点?我觉得这种平台应该朝着更加简单好用、低代码(或者 0 代码)的方向去走

stevenxu 回复

测试平台应该做成什么样,我觉得还是看公司业务和团队构成。如果是需要让只会功能测试或者不愿接触代码的同事用起来的话,那么就要做成填表格或者拖拽形式的。我们这边业务并不复杂,除了 http 协议,还涉及到 websocket、protobuf 等协议,只用 requests 封装成前端控件来操作,扩展性可能不太够,加上测试团队都会写 Python 也愿意用这种方式做自动化,pytest 写起来还算轻松,简单直接高效,就采取了这种方式。小公司小团队。

stevenxu 回复

还要一个重要原因是,通过 Git 来管理,不太好统计和跟踪,不方便共享后运行,做成平台能解决这些问题。实际上我们代码还是在 PyCharm 来写的,平台提供了本地下载的功能,可以把配置好了环境变量和 fixtures 打包到本地使用。第一次写代码是在本地,调试跑通后,复制粘贴到平台管理,后续维护和别人使用都能在平台上快捷操作。

dongfanger 回复

《学习版 pytest 内核测试平台开发万字长文入门篇》已发布,访问地址:
https://dongfanger.gitee.io/blog/chapters/teprunner.html 这个报 404 了!

ssx 回复
ttps://dongfanger.gitee.io/blog/chapters/teprunner开源测试平台.html

可以访问这个。

ssx 回复
dongfanger 回复

好的,谢谢

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