HTMLReport


NameHTMLReport JSON
Version 2.4.0 PyPI version JSON
download
home_pagehttps://github.com/liushilive/htmlreport
SummaryPython3 Unittest HTML报告生成器
upload_time2023-11-28 13:37:58
maintainer
docs_urlNone
author刘士
requires_python
licenseApache 2.0
keywords htmltestrunner test runner html reports unittest
VCS
bugtrack_url
requirements No requirements were recorded.
Travis-CI No Travis.
coveralls test coverage No coveralls.
            # HTMLReport

[![Downloads](https://pepy.tech/badge/htmlreport)](https://pepy.tech/project/htmlreport)

`HTMLReport`是一个单元测试测试运行器,可以将测试结果保存在 Html 文件中,用于人性化的结果显示。

仅支持**Python 3.x**

* [The report template](https://liushilive.github.io/report/report/#en)

* [报告样板](https://liushilive.github.io/report/report/#cn)

## 安装

要安装 HTMLReport,请在终端中运行此命令

```bash
pip install HTMLReport
```

这是安装HTMLReport的首选方法,因为它将始终安装最新的稳定版本。 如果您没有安装 [pip](https://pip.pypa.io/),
则该 [Python安装指南](http://docs.python-guide.org/en/latest/starting/installation/ "Python安装指南")
可以指导您完成该过程。

## 更新日志

### 2023-11-15 新增功能 - by Joffrey

1、按用例组多线程执行

2、在报告中添加视频

## 使用方法

### 按用例组多线程执行

在 run 函数中传入 threadSuite 参数,threadSuite 为用例组的数量,如下:

```python
suiteList=[]
suiteList.append(unittest.TestLoader().loadTestsFromTestCase(testcase_class))

HTMLReport.TestRunner(
    title="Web端UI自动测试",
    description=f'运行环境: <span class="info">{args.env.upper()}</span></br>地址: <span class="info">{url}</span>',
    output_path=f'report/{args.timeStr}',
    report_file_name='index',
    sequential_execution=0,tries=2,delay=3,retry=True,
).run(unittest.TestSuite(suiteList),threadSuite=len(suiteList))
```

### 在报告中添加视频

需要使用 playwright 作为驱动

```python
from HTMLReport import addImage,addVideos
from playwright.sync_api import sync_playwright

class Test01_CLASS(ParameTestCase):
    @classmethod
    def setUpClass(cls):
        logging.info(' ########## 测试开始 ########## ')
        cls.playwright=sync_playwright().start()
        cls.pw_browser=cls.playwright.chromium.launch()
        cls.size={"width":1980,"height":1260}
        cls.URL="http://www.baidu.com"
        
    def setUp(self):
        self.context=self.pw_browser.new_context(record_video_dir=f'report/{self.args.timeStr}/videos/',record_video_size=self.size) # 开始录屏
        self.pw_page=self.context.new_page()
        self.pw_page.set_viewport_size(self.size)
        self.pw_page.goto(self.URL)
        add_context_cookie(self.context,readCookie())


    def test_01(self,productCode):
        '''test_01 标题描述'''
        doSomethings.......
        
        # 使用 playwright 截图
        addImage(base64.b64encode(self.pw_page.screenshot()).decode(),f'test 截图1')

        doSomethings.......
        logging.info('测试结束')



    def tearDown(self):
        self.context.close() # 结束录屏
        addVideos(self.pw_page.video.path().split('/')[-1]) # 添加视频到报告

    @classmethod
    def tearDownClass(cls):
        cls.pw_browser.close()
        cls.playwright.stop()
        logging.info(' ########## 测试结束 ########## ')

if __name__=='__main__':
    unittest.main()
```

### 日志

为测试报告中添加过程日志,在多线程下,在报告中会分别记录每个线程的日志,同时会产生与测试报告同名的测试 log 文件。

```python
import logging

logging.info("测试")
logging.debug("测试")
logging.warning("测试")
logging.error("测试")
logging.critical("测试")
```

### 图片信息

为测试报告添加图片信息,请将图片信息编码为 base64 编码。

如采用的是 selenium 截屏,请使用 `get_screenshot_as_base64()` 方法获取 base64 encoded string 作为参数传入。

本库会自动将图片保存在报告路径下的`images`目录下,并嵌入到报告中。

```python
import base64
from HTMLReport import add_image

with open("baidu.png", 'rb') as f:
    image = base64.b64encode(f.read())
    add_image(image, "图片标题", "图片描述")
```

* `image` 参数可以控制全局是否添加图片
* `failed_image` 参数可以控制是否只在测试失败时保存图片

### 失败重试

测试方法前加入装饰器 `@retry` `@no_retry`,用于重试与不重试

### 数据驱动

测试类前加入装饰器 `@ddt.ddt`

测试方法前加入装饰器 `@ddt.data(*data)`

### 实例

```python
import base64
import logging
import random
import unittest

from HTMLReport import ddt, TestRunner, add_image, no_retry, retry


class TestOne(unittest.TestCase):
    """常规测试"""

    def setUp(self) -> None:
        logging.debug("测试开始")
        logging.info("测试开始")
        logging.warning("测试开始")
        logging.error("测试开始")

    def tearDown(self) -> None:
        logging.info("测试结束")

    def test_true(self):
        """
        测试通过
        """
        self.assertTrue(True)

    def test_false(self):
        """
        测试失败

        :return:
        """
        self.assertTrue(False)

    def test_error(self):
        """
        测试异常

        :return:
        """
        self.assertTrue(int("5.2"))

    @unittest.skip("跳过用例")
    def test_skip(self):
        """
        测试跳过

        :return:
        """
        self.assertTrue(int("5.2"))

    def test_skip_(self):
        """
        测试中途跳过

        :return:
        """
        logging.info("准备跳过")
        self.skipTest("中途跳过")
        self.assertTrue(int("5.2"))

    def test_image(self):
        """测试截图"""
        with open("baidu.png", 'rb') as f:
            image = base64.b64encode(f.read())
            alt = """百度一下,你就知道了。"""
            for i in range(5):
                add_image(image, f"百度 {i}", alt)


@ddt.ddt
class TestDDT(unittest.TestCase):
    """DDT 测试"""

    def setUp(self) -> None:
        logging.info("测试开始")

    def tearDown(self) -> None:
        logging.info("测试结束")

    @ddt.data(*range(3))
    def test_a(self, n):
        self.assertEqual(n, random.randint(0, 2))


@ddt.ddt
class TestNoRetry(unittest.TestCase):
    """测试 DDT 不重试"""

    def setUp(self) -> None:
        logging.info("测试开始")

    def tearDown(self) -> None:
        logging.info("测试结束")

    @no_retry
    @ddt.data(*range(3))
    def test_a(self, n):
        self.assertEqual(n, random.randint(0, 2))


@ddt.ddt
class TestRetry(unittest.TestCase):
    """测试 DDT 重试"""

    def setUp(self) -> None:
        logging.info("测试开始")

    def tearDown(self) -> None:
        logging.info("测试结束")

    @retry
    @ddt.data(*range(3))
    def test_a(self, n):
        self.assertEqual(n, random.randint(0, 2))


class TestClassMethod(unittest.TestCase):
    """
    测试 setUpClass
    """

    n = 0

    @classmethod
    def setUpClass(cls) -> None:
        logging.info(f"初始计数:{cls.n}")

    @classmethod
    def tearDownClass(cls) -> None:
        logging.info(f"最终计数:{cls.n}")

    def setUp(self) -> None:
        logging.info(f"前置计数:{self.n}")

    def tearDown(self) -> None:
        logging.info(f"后置计数:{self.n}")

    def test_1(self):
        self.__class__.n += 1
        logging.info(f"运行修改:{self.n}")

    def test_2(self):
        self.__class__.n += 1
        logging.info(f"运行修改:{self.n}")
        self.assertTrue(False)


if __name__ == '__main__':
    test_runner = TestRunner(
        report_file_name="index",
        output_path="report",
        title="一个简单的测试报告",
        description="随意描述",
        thread_count=5,
        thread_start_wait=0.1,
        tries=3,
        delay=0,
        back_off=1,
        retry=True,
        sequential_execution=True,
        lang="cn"
    )
    suite = unittest.TestSuite()
    suite_sub = unittest.TestSuite()
    loader = unittest.TestLoader()
    suite_sub.addTests(loader.loadTestsFromTestCase(TestOne))
    suite_sub.addTests(loader.loadTestsFromTestCase(TestDDT))
    suite.addTests(suite_sub)
    suite.addTests(loader.loadTestsFromTestCase(TestRetry))
    suite.addTests(loader.loadTestsFromTestCase(TestNoRetry))
    suite.addTests(loader.loadTestsFromTestCase(TestClassMethod))
    suite.addTests(loader.loadTestsFromNames(["HTMLReport_test.TestClassMethod"]))
    test_runner.run(suite)

```

> 如果您有其他的需求,请发邮件给我:<liushilive@outlook.com> , 祝您使用愉快!

Links:

---------

* [https://liushilive.github.io](https://liushilive.github.io "Github")

            

Raw data

            {
    "_id": null,
    "home_page": "https://github.com/liushilive/htmlreport",
    "name": "HTMLReport",
    "maintainer": "",
    "docs_url": null,
    "requires_python": "",
    "maintainer_email": "",
    "keywords": "HtmlTestRunner test runner html reports unittest",
    "author": "\u5218\u58eb",
    "author_email": "liushilive@outlook.com",
    "download_url": "https://files.pythonhosted.org/packages/1b/64/d95e47fca243b4c213adc4020c8b14d158b2f71a61f27edf1b4fc7062561/HTMLReport-2.4.0.tar.gz",
    "platform": null,
    "description": "# HTMLReport\r\n\r\n[![Downloads](https://pepy.tech/badge/htmlreport)](https://pepy.tech/project/htmlreport)\r\n\r\n`HTMLReport`\u662f\u4e00\u4e2a\u5355\u5143\u6d4b\u8bd5\u6d4b\u8bd5\u8fd0\u884c\u5668\uff0c\u53ef\u4ee5\u5c06\u6d4b\u8bd5\u7ed3\u679c\u4fdd\u5b58\u5728 Html \u6587\u4ef6\u4e2d\uff0c\u7528\u4e8e\u4eba\u6027\u5316\u7684\u7ed3\u679c\u663e\u793a\u3002\r\n\r\n\u4ec5\u652f\u6301**Python 3.x**\r\n\r\n* [The report template](https://liushilive.github.io/report/report/#en)\r\n\r\n* [\u62a5\u544a\u6837\u677f](https://liushilive.github.io/report/report/#cn)\r\n\r\n## \u5b89\u88c5\r\n\r\n\u8981\u5b89\u88c5 HTMLReport\uff0c\u8bf7\u5728\u7ec8\u7aef\u4e2d\u8fd0\u884c\u6b64\u547d\u4ee4\r\n\r\n```bash\r\npip install HTMLReport\r\n```\r\n\r\n\u8fd9\u662f\u5b89\u88c5HTMLReport\u7684\u9996\u9009\u65b9\u6cd5\uff0c\u56e0\u4e3a\u5b83\u5c06\u59cb\u7ec8\u5b89\u88c5\u6700\u65b0\u7684\u7a33\u5b9a\u7248\u672c\u3002 \u5982\u679c\u60a8\u6ca1\u6709\u5b89\u88c5 [pip](https://pip.pypa.io/)\uff0c\r\n\u5219\u8be5 [Python\u5b89\u88c5\u6307\u5357](http://docs.python-guide.org/en/latest/starting/installation/ \"Python\u5b89\u88c5\u6307\u5357\")\r\n\u53ef\u4ee5\u6307\u5bfc\u60a8\u5b8c\u6210\u8be5\u8fc7\u7a0b\u3002\r\n\r\n## \u66f4\u65b0\u65e5\u5fd7\r\n\r\n### 2023-11-15 \u65b0\u589e\u529f\u80fd - by Joffrey\r\n\r\n1\u3001\u6309\u7528\u4f8b\u7ec4\u591a\u7ebf\u7a0b\u6267\u884c\r\n\r\n2\u3001\u5728\u62a5\u544a\u4e2d\u6dfb\u52a0\u89c6\u9891\r\n\r\n## \u4f7f\u7528\u65b9\u6cd5\r\n\r\n### \u6309\u7528\u4f8b\u7ec4\u591a\u7ebf\u7a0b\u6267\u884c\r\n\r\n\u5728 run \u51fd\u6570\u4e2d\u4f20\u5165 threadSuite \u53c2\u6570\uff0cthreadSuite \u4e3a\u7528\u4f8b\u7ec4\u7684\u6570\u91cf\uff0c\u5982\u4e0b\uff1a\r\n\r\n```python\r\nsuiteList=[]\r\nsuiteList.append(unittest.TestLoader().loadTestsFromTestCase(testcase_class))\r\n\r\nHTMLReport.TestRunner(\r\n    title=\"Web\u7aefUI\u81ea\u52a8\u6d4b\u8bd5\",\r\n    description=f'\u8fd0\u884c\u73af\u5883: <span class=\"info\">{args.env.upper()}</span></br>\u5730\u5740: <span class=\"info\">{url}</span>',\r\n    output_path=f'report/{args.timeStr}',\r\n    report_file_name='index',\r\n    sequential_execution=0,tries=2,delay=3,retry=True,\r\n).run(unittest.TestSuite(suiteList),threadSuite=len(suiteList))\r\n```\r\n\r\n### \u5728\u62a5\u544a\u4e2d\u6dfb\u52a0\u89c6\u9891\r\n\r\n\u9700\u8981\u4f7f\u7528 playwright \u4f5c\u4e3a\u9a71\u52a8\r\n\r\n```python\r\nfrom HTMLReport import addImage,addVideos\r\nfrom playwright.sync_api import sync_playwright\r\n\r\nclass Test01_CLASS(ParameTestCase):\r\n    @classmethod\r\n    def setUpClass(cls):\r\n        logging.info(' ########## \u6d4b\u8bd5\u5f00\u59cb ########## ')\r\n        cls.playwright=sync_playwright().start()\r\n        cls.pw_browser=cls.playwright.chromium.launch()\r\n        cls.size={\"width\":1980,\"height\":1260}\r\n        cls.URL=\"http://www.baidu.com\"\r\n        \r\n    def setUp(self):\r\n        self.context=self.pw_browser.new_context(record_video_dir=f'report/{self.args.timeStr}/videos/',record_video_size=self.size) # \u5f00\u59cb\u5f55\u5c4f\r\n        self.pw_page=self.context.new_page()\r\n        self.pw_page.set_viewport_size(self.size)\r\n        self.pw_page.goto(self.URL)\r\n        add_context_cookie(self.context,readCookie())\r\n\r\n\r\n    def test_01(self,productCode):\r\n        '''test_01 \u6807\u9898\u63cf\u8ff0'''\r\n        doSomethings.......\r\n        \r\n        # \u4f7f\u7528 playwright \u622a\u56fe\r\n        addImage(base64.b64encode(self.pw_page.screenshot()).decode(),f'test \u622a\u56fe1')\r\n\r\n        doSomethings.......\r\n        logging.info('\u6d4b\u8bd5\u7ed3\u675f')\r\n\r\n\r\n\r\n    def tearDown(self):\r\n        self.context.close() # \u7ed3\u675f\u5f55\u5c4f\r\n        addVideos(self.pw_page.video.path().split('/')[-1]) # \u6dfb\u52a0\u89c6\u9891\u5230\u62a5\u544a\r\n\r\n    @classmethod\r\n    def tearDownClass(cls):\r\n        cls.pw_browser.close()\r\n        cls.playwright.stop()\r\n        logging.info(' ########## \u6d4b\u8bd5\u7ed3\u675f ########## ')\r\n\r\nif __name__=='__main__':\r\n    unittest.main()\r\n```\r\n\r\n### \u65e5\u5fd7\r\n\r\n\u4e3a\u6d4b\u8bd5\u62a5\u544a\u4e2d\u6dfb\u52a0\u8fc7\u7a0b\u65e5\u5fd7\uff0c\u5728\u591a\u7ebf\u7a0b\u4e0b\uff0c\u5728\u62a5\u544a\u4e2d\u4f1a\u5206\u522b\u8bb0\u5f55\u6bcf\u4e2a\u7ebf\u7a0b\u7684\u65e5\u5fd7\uff0c\u540c\u65f6\u4f1a\u4ea7\u751f\u4e0e\u6d4b\u8bd5\u62a5\u544a\u540c\u540d\u7684\u6d4b\u8bd5 log \u6587\u4ef6\u3002\r\n\r\n```python\r\nimport logging\r\n\r\nlogging.info(\"\u6d4b\u8bd5\")\r\nlogging.debug(\"\u6d4b\u8bd5\")\r\nlogging.warning(\"\u6d4b\u8bd5\")\r\nlogging.error(\"\u6d4b\u8bd5\")\r\nlogging.critical(\"\u6d4b\u8bd5\")\r\n```\r\n\r\n### \u56fe\u7247\u4fe1\u606f\r\n\r\n\u4e3a\u6d4b\u8bd5\u62a5\u544a\u6dfb\u52a0\u56fe\u7247\u4fe1\u606f\uff0c\u8bf7\u5c06\u56fe\u7247\u4fe1\u606f\u7f16\u7801\u4e3a base64 \u7f16\u7801\u3002\r\n\r\n\u5982\u91c7\u7528\u7684\u662f selenium \u622a\u5c4f\uff0c\u8bf7\u4f7f\u7528 `get_screenshot_as_base64()` \u65b9\u6cd5\u83b7\u53d6 base64 encoded string \u4f5c\u4e3a\u53c2\u6570\u4f20\u5165\u3002\r\n\r\n\u672c\u5e93\u4f1a\u81ea\u52a8\u5c06\u56fe\u7247\u4fdd\u5b58\u5728\u62a5\u544a\u8def\u5f84\u4e0b\u7684`images`\u76ee\u5f55\u4e0b\uff0c\u5e76\u5d4c\u5165\u5230\u62a5\u544a\u4e2d\u3002\r\n\r\n```python\r\nimport base64\r\nfrom HTMLReport import add_image\r\n\r\nwith open(\"baidu.png\", 'rb') as f:\r\n    image = base64.b64encode(f.read())\r\n    add_image(image, \"\u56fe\u7247\u6807\u9898\", \"\u56fe\u7247\u63cf\u8ff0\")\r\n```\r\n\r\n* `image` \u53c2\u6570\u53ef\u4ee5\u63a7\u5236\u5168\u5c40\u662f\u5426\u6dfb\u52a0\u56fe\u7247\r\n* `failed_image` \u53c2\u6570\u53ef\u4ee5\u63a7\u5236\u662f\u5426\u53ea\u5728\u6d4b\u8bd5\u5931\u8d25\u65f6\u4fdd\u5b58\u56fe\u7247\r\n\r\n### \u5931\u8d25\u91cd\u8bd5\r\n\r\n\u6d4b\u8bd5\u65b9\u6cd5\u524d\u52a0\u5165\u88c5\u9970\u5668 `@retry` `@no_retry`\uff0c\u7528\u4e8e\u91cd\u8bd5\u4e0e\u4e0d\u91cd\u8bd5\r\n\r\n### \u6570\u636e\u9a71\u52a8\r\n\r\n\u6d4b\u8bd5\u7c7b\u524d\u52a0\u5165\u88c5\u9970\u5668 `@ddt.ddt`\r\n\r\n\u6d4b\u8bd5\u65b9\u6cd5\u524d\u52a0\u5165\u88c5\u9970\u5668 `@ddt.data(*data)`\r\n\r\n### \u5b9e\u4f8b\r\n\r\n```python\r\nimport base64\r\nimport logging\r\nimport random\r\nimport unittest\r\n\r\nfrom HTMLReport import ddt, TestRunner, add_image, no_retry, retry\r\n\r\n\r\nclass TestOne(unittest.TestCase):\r\n    \"\"\"\u5e38\u89c4\u6d4b\u8bd5\"\"\"\r\n\r\n    def setUp(self) -> None:\r\n        logging.debug(\"\u6d4b\u8bd5\u5f00\u59cb\")\r\n        logging.info(\"\u6d4b\u8bd5\u5f00\u59cb\")\r\n        logging.warning(\"\u6d4b\u8bd5\u5f00\u59cb\")\r\n        logging.error(\"\u6d4b\u8bd5\u5f00\u59cb\")\r\n\r\n    def tearDown(self) -> None:\r\n        logging.info(\"\u6d4b\u8bd5\u7ed3\u675f\")\r\n\r\n    def test_true(self):\r\n        \"\"\"\r\n        \u6d4b\u8bd5\u901a\u8fc7\r\n        \"\"\"\r\n        self.assertTrue(True)\r\n\r\n    def test_false(self):\r\n        \"\"\"\r\n        \u6d4b\u8bd5\u5931\u8d25\r\n\r\n        :return:\r\n        \"\"\"\r\n        self.assertTrue(False)\r\n\r\n    def test_error(self):\r\n        \"\"\"\r\n        \u6d4b\u8bd5\u5f02\u5e38\r\n\r\n        :return:\r\n        \"\"\"\r\n        self.assertTrue(int(\"5.2\"))\r\n\r\n    @unittest.skip(\"\u8df3\u8fc7\u7528\u4f8b\")\r\n    def test_skip(self):\r\n        \"\"\"\r\n        \u6d4b\u8bd5\u8df3\u8fc7\r\n\r\n        :return:\r\n        \"\"\"\r\n        self.assertTrue(int(\"5.2\"))\r\n\r\n    def test_skip_(self):\r\n        \"\"\"\r\n        \u6d4b\u8bd5\u4e2d\u9014\u8df3\u8fc7\r\n\r\n        :return:\r\n        \"\"\"\r\n        logging.info(\"\u51c6\u5907\u8df3\u8fc7\")\r\n        self.skipTest(\"\u4e2d\u9014\u8df3\u8fc7\")\r\n        self.assertTrue(int(\"5.2\"))\r\n\r\n    def test_image(self):\r\n        \"\"\"\u6d4b\u8bd5\u622a\u56fe\"\"\"\r\n        with open(\"baidu.png\", 'rb') as f:\r\n            image = base64.b64encode(f.read())\r\n            alt = \"\"\"\u767e\u5ea6\u4e00\u4e0b\uff0c\u4f60\u5c31\u77e5\u9053\u4e86\u3002\"\"\"\r\n            for i in range(5):\r\n                add_image(image, f\"\u767e\u5ea6 {i}\", alt)\r\n\r\n\r\n@ddt.ddt\r\nclass TestDDT(unittest.TestCase):\r\n    \"\"\"DDT \u6d4b\u8bd5\"\"\"\r\n\r\n    def setUp(self) -> None:\r\n        logging.info(\"\u6d4b\u8bd5\u5f00\u59cb\")\r\n\r\n    def tearDown(self) -> None:\r\n        logging.info(\"\u6d4b\u8bd5\u7ed3\u675f\")\r\n\r\n    @ddt.data(*range(3))\r\n    def test_a(self, n):\r\n        self.assertEqual(n, random.randint(0, 2))\r\n\r\n\r\n@ddt.ddt\r\nclass TestNoRetry(unittest.TestCase):\r\n    \"\"\"\u6d4b\u8bd5 DDT \u4e0d\u91cd\u8bd5\"\"\"\r\n\r\n    def setUp(self) -> None:\r\n        logging.info(\"\u6d4b\u8bd5\u5f00\u59cb\")\r\n\r\n    def tearDown(self) -> None:\r\n        logging.info(\"\u6d4b\u8bd5\u7ed3\u675f\")\r\n\r\n    @no_retry\r\n    @ddt.data(*range(3))\r\n    def test_a(self, n):\r\n        self.assertEqual(n, random.randint(0, 2))\r\n\r\n\r\n@ddt.ddt\r\nclass TestRetry(unittest.TestCase):\r\n    \"\"\"\u6d4b\u8bd5 DDT \u91cd\u8bd5\"\"\"\r\n\r\n    def setUp(self) -> None:\r\n        logging.info(\"\u6d4b\u8bd5\u5f00\u59cb\")\r\n\r\n    def tearDown(self) -> None:\r\n        logging.info(\"\u6d4b\u8bd5\u7ed3\u675f\")\r\n\r\n    @retry\r\n    @ddt.data(*range(3))\r\n    def test_a(self, n):\r\n        self.assertEqual(n, random.randint(0, 2))\r\n\r\n\r\nclass TestClassMethod(unittest.TestCase):\r\n    \"\"\"\r\n    \u6d4b\u8bd5 setUpClass\r\n    \"\"\"\r\n\r\n    n = 0\r\n\r\n    @classmethod\r\n    def setUpClass(cls) -> None:\r\n        logging.info(f\"\u521d\u59cb\u8ba1\u6570\uff1a{cls.n}\")\r\n\r\n    @classmethod\r\n    def tearDownClass(cls) -> None:\r\n        logging.info(f\"\u6700\u7ec8\u8ba1\u6570\uff1a{cls.n}\")\r\n\r\n    def setUp(self) -> None:\r\n        logging.info(f\"\u524d\u7f6e\u8ba1\u6570\uff1a{self.n}\")\r\n\r\n    def tearDown(self) -> None:\r\n        logging.info(f\"\u540e\u7f6e\u8ba1\u6570\uff1a{self.n}\")\r\n\r\n    def test_1(self):\r\n        self.__class__.n += 1\r\n        logging.info(f\"\u8fd0\u884c\u4fee\u6539\uff1a{self.n}\")\r\n\r\n    def test_2(self):\r\n        self.__class__.n += 1\r\n        logging.info(f\"\u8fd0\u884c\u4fee\u6539\uff1a{self.n}\")\r\n        self.assertTrue(False)\r\n\r\n\r\nif __name__ == '__main__':\r\n    test_runner = TestRunner(\r\n        report_file_name=\"index\",\r\n        output_path=\"report\",\r\n        title=\"\u4e00\u4e2a\u7b80\u5355\u7684\u6d4b\u8bd5\u62a5\u544a\",\r\n        description=\"\u968f\u610f\u63cf\u8ff0\",\r\n        thread_count=5,\r\n        thread_start_wait=0.1,\r\n        tries=3,\r\n        delay=0,\r\n        back_off=1,\r\n        retry=True,\r\n        sequential_execution=True,\r\n        lang=\"cn\"\r\n    )\r\n    suite = unittest.TestSuite()\r\n    suite_sub = unittest.TestSuite()\r\n    loader = unittest.TestLoader()\r\n    suite_sub.addTests(loader.loadTestsFromTestCase(TestOne))\r\n    suite_sub.addTests(loader.loadTestsFromTestCase(TestDDT))\r\n    suite.addTests(suite_sub)\r\n    suite.addTests(loader.loadTestsFromTestCase(TestRetry))\r\n    suite.addTests(loader.loadTestsFromTestCase(TestNoRetry))\r\n    suite.addTests(loader.loadTestsFromTestCase(TestClassMethod))\r\n    suite.addTests(loader.loadTestsFromNames([\"HTMLReport_test.TestClassMethod\"]))\r\n    test_runner.run(suite)\r\n\r\n```\r\n\r\n> \u5982\u679c\u60a8\u6709\u5176\u4ed6\u7684\u9700\u6c42\uff0c\u8bf7\u53d1\u90ae\u4ef6\u7ed9\u6211\uff1a<liushilive@outlook.com> \uff0c \u795d\u60a8\u4f7f\u7528\u6109\u5feb\uff01\r\n\r\nLinks:\r\n\r\n---------\r\n\r\n* [https://liushilive.github.io](https://liushilive.github.io \"Github\")\r\n",
    "bugtrack_url": null,
    "license": "Apache 2.0",
    "summary": "Python3 Unittest HTML\u62a5\u544a\u751f\u6210\u5668",
    "version": "2.4.0",
    "project_urls": {
        "Homepage": "https://github.com/liushilive/htmlreport",
        "The report template": "https://liushilive.github.io/report/report/#en",
        "issues": "https://github.com/liushilive/htmlreport/issues",
        "\u4e2a\u4eba\u4e3b\u9875": "https://liushilive.github.io/",
        "\u62a5\u544a\u6837\u677f": "https://liushilive.github.io/report/report/#cn"
    },
    "split_keywords": [
        "htmltestrunner",
        "test",
        "runner",
        "html",
        "reports",
        "unittest"
    ],
    "urls": [
        {
            "comment_text": "",
            "digests": {
                "blake2b_256": "4aa0b7226087bff74b2539093581800acd698264b160a92e352d8e2eb3071452",
                "md5": "5e051c0f639ff21dac8519c489f27771",
                "sha256": "454402bcfecf9ac2b982c1508d7491b8d071d9b1dbd95f4bbd78dc8382fd2f6b"
            },
            "downloads": -1,
            "filename": "HTMLReport-2.4.0-py3-none-any.whl",
            "has_sig": false,
            "md5_digest": "5e051c0f639ff21dac8519c489f27771",
            "packagetype": "bdist_wheel",
            "python_version": "py3",
            "requires_python": null,
            "size": 35212,
            "upload_time": "2023-11-28T13:37:56",
            "upload_time_iso_8601": "2023-11-28T13:37:56.374931Z",
            "url": "https://files.pythonhosted.org/packages/4a/a0/b7226087bff74b2539093581800acd698264b160a92e352d8e2eb3071452/HTMLReport-2.4.0-py3-none-any.whl",
            "yanked": false,
            "yanked_reason": null
        },
        {
            "comment_text": "",
            "digests": {
                "blake2b_256": "1b64d95e47fca243b4c213adc4020c8b14d158b2f71a61f27edf1b4fc7062561",
                "md5": "c96df8a74a763ccb206b912c9e690d94",
                "sha256": "d2d3e186aea1c88e347a075c2dd3d402bb37abf2858c1e08534dee18ee1f41ca"
            },
            "downloads": -1,
            "filename": "HTMLReport-2.4.0.tar.gz",
            "has_sig": false,
            "md5_digest": "c96df8a74a763ccb206b912c9e690d94",
            "packagetype": "sdist",
            "python_version": "source",
            "requires_python": null,
            "size": 32824,
            "upload_time": "2023-11-28T13:37:58",
            "upload_time_iso_8601": "2023-11-28T13:37:58.348016Z",
            "url": "https://files.pythonhosted.org/packages/1b/64/d95e47fca243b4c213adc4020c8b14d158b2f71a61f27edf1b4fc7062561/HTMLReport-2.4.0.tar.gz",
            "yanked": false,
            "yanked_reason": null
        }
    ],
    "upload_time": "2023-11-28 13:37:58",
    "github": true,
    "gitlab": false,
    "bitbucket": false,
    "codeberg": false,
    "github_user": "liushilive",
    "github_project": "htmlreport",
    "travis_ci": false,
    "coveralls": false,
    "github_actions": false,
    "lcname": "htmlreport"
}
        
Elapsed time: 0.14359s