PyWebReport,我写了一个测试报告框架

2022-07-21 21:10:00

为什么我要写这个报告框架

市面上没有一个让我满意的框架,我正在试图寻找一个 能兼容各种测试框架可离线生成具有现代美观风格 的报告
但是调研了一圈发现没有一个报告框架能满足我的需求,于是我准备写一个这样的报告

我知道这个时候,有很多朋友会跳出来说: allure,XTestRunnner、…了解一下?
我是有使用这些报告的,我也承认并赞赏它们的优秀。但是他们都有不好的地方,至于哪里不好,这里不做细说,因为可能你说一个东西不好,那么可能会跳出一千一万个反驳的声音。这篇文的目的并不是拿其他报告做对比,互掐优缺点。

我仅仅是想写一个我心目中的测试报告框架。仅此而已。

介绍

好的,那么来介绍下 PyWebRerpot

首先我总结一下他的亮点 ✨:

  • 🌺 支持pytest、unittest等主流测试框架
  • 🌺 真正的可离线,即你拔掉网线也是可以生成报告的,这对于一些只有内网环境的公司可能是福音
  • 🌺 灵活且自由的,不限于测试框架,只要给一个符合格式化规定的json数据,就能生成报告

那么再来看看pywebreport 报告的概览,你可以访问 https://yongchin0821.github.io/pywebreport/#dashboard 直接查看

使用说明

安装

# pip install pywebreport

pytest

  • 熟悉的方式运行即可,注意 --report参数,后跟报告生成的路径
# pytest -q --report result/report.html
  • .py 脚本里面也可以像这样运行:
import pytest

if __name__ == '__main__':
    args = ['./', '-q', '--report', 'result/report.html']
    pytest.main(args)

unitest

import the WebReportRunner, 然后把 TestSuite 丢给 WebReportRunner
无论你是用哪种方式收集的TestSuite ,你只需要把 TestSuite 放到 WebReportRunner()里面去就可以了。注意参数 report,可以指定报告的路径

import unittest
from test_success import UnitTestSuccessCase
from pywebreport import WebReportRunner


if __name__ == '__main__':
    suite = unittest.TestSuite()
    loader = unittest.TestLoader()

    # suite.addTest(UnitTestSuccessCase("test_case2"))
    # suite.addTest(UnitTestSuccessCase("test_err_print"))
    # suite.addTest(loader.loadTestsFromTestCase(UnitTestCase))
    # suite = loader.loadTestsFromTestCase(UnitTestCase)

    suite.addTest(loader.discover("."))

    runner = WebReportRunner(report="result/report.html")
    test_result = runner.run(suite)

other

诚然,在设计之初,就考虑到了兼容问题,未来可能会面临各种测试框架,数据来源是不确定的。那么如果能拟定一组格式化的数据范本,只要按照规定格式给到数据,也是可以使用pywebreport生成报告的,比如下列这串json数据:

{
  "title": "PyWebReport",
  "path": "/Users/yongchin/someplace/pytest/result/report.html",
  "result": {
    "total": "8",
    "passed": "4",
    "failed": "3",
    "warnings": "1",
    "error": "1",
    "skipped": "1",
    "duration": "0.09765386581420898",
    "deselected": "0",
    "rate_passed": "50.00%",
    "rate_failed": "37.50%",
    "rate_warnings": "12.50%",
    "rate_skipped": "12.50%"
  },
  "suites": {
    "test/testsuites/pytest/test_fail.py": {
      "cases": {
        "test_case1": {
          "id": "test/testsuites/pytest/test_fail.py::test_case1",
          "desc": "test fail",
          "status": "failed",
          "duration": 0.0,
          "className": "",
          "consoleLog": [],
          "errMsg": "def test_case1():\n        \"\"\"test fail\"\"\"\n>       assert 0\nE       assert 0\n\ntest_fail.py:10: AssertionError",
          "execTime": "2022-07-20 10:33:54"
        },
        "test_case2": {
          "id": "test/testsuites/pytest/test_fail.py::test_case2",
          "desc": "\u3053\u3093\u306b\u3061\u306f\u4e16\u754c",
          "status": "failed",
          "duration": 0.0,
          "className": "",
          "consoleLog": [],
          "errMsg": "def test_case2():\n        \"\"\"\u3053\u3093\u306b\u3061\u306f\u4e16\u754c\"\"\"\n>       raise IOError(123)\nE       OSError: 123\n\ntest_fail.py:15: OSError",
          "execTime": "2022-07-20 10:33:54"
        },
        "test_case3": {
          "id": "test/testsuites/pytest/test_fail.py::test_case3",
          "desc": "test error",
          "status": "failed",
          "duration": 0.0,
          "className": "",
          "consoleLog": [],
          "errMsg": "@pytest.fixture()\n    def user():\n        a = \"yoyo\"\n>       assert a == \"yoyo123\"  # fixture failed with error\nE       AssertionError: assert 'yoyo' == 'yoyo123'\nE         - yoyo123\nE         + yoyo\n\ntest_fail.py:21: AssertionError",
          "execTime": "2022-07-20 10:33:54"
        }
      },
      "results": {
        "counts": 3,
        "passed": 0,
        "failed": 3,
        "warnings": 0,
        "error": 0,
        "skipped": 0,
        "rate_passed": "0.00%"
      },
      "duration": 0.0
    },
    "test/testsuites/pytest/test_success.py": {
      "cases": {
        "test_case1": {
          "id": "test/testsuites/pytest/test_success.py::TestA::test_case1",
          "desc": "test pass",
          "status": "passed",
          "duration": 0.0,
          "className": "TestA",
          "consoleLog": [],
          "errMsg": "",
          "execTime": "2022-07-20 10:33:54"
        },
      },
      "results": {
        "counts": 5,
        "passed": 4,
        "failed": 0,
        "warnings": 0,
        "error": 0,
        "skipped": 1,
        "rate_passed": "80.00%"
      },
      "duration": 0.0
    }
  }
}

只要满足以上格式的json数据, 调用pywebreport的 API, 就可以生成报告

from pywebreport import mkrepot

datas = {
  "title": "PyWebReport",
  "path": "report.html",
  "result": {...},
  "suites": {...}
}

mkrepot(datas)

招募

✨ 小小的一则广告

🐣 这是一个孵化性项目

💖 如果您对这个项目感兴趣,诚挚邀请您的加入

  • 只要对页面美观有独特了解,能分享或设计页面经验
  • 只要比较熟悉pytest/unittest

最主要的是你热爱开源,希望为开源做贡献。