{"id":20197105,"url":"https://github.com/githublitao/htmltestrunner_chart","last_synced_at":"2025-08-19T21:31:24.208Z","repository":{"id":41262799,"uuid":"149058899","full_name":"githublitao/HTMLTestRunner_Chart","owner":"githublitao","description":"基于unittest的测试报告，柱状图，饼图，错误截图，失败重试等功能","archived":false,"fork":false,"pushed_at":"2023-04-23T08:56:27.000Z","size":11015,"stargazers_count":127,"open_issues_count":0,"forks_count":66,"subscribers_count":6,"default_branch":"master","last_synced_at":"2024-12-07T22:30:55.284Z","etag":null,"topics":[],"latest_commit_sha":null,"homepage":"","language":"Python","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/githublitao.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"license","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null}},"created_at":"2018-09-17T02:37:59.000Z","updated_at":"2024-07-21T12:15:21.000Z","dependencies_parsed_at":"2024-11-14T04:37:25.979Z","dependency_job_id":null,"html_url":"https://github.com/githublitao/HTMLTestRunner_Chart","commit_stats":null,"previous_names":[],"tags_count":0,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/githublitao%2FHTMLTestRunner_Chart","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/githublitao%2FHTMLTestRunner_Chart/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/githublitao%2FHTMLTestRunner_Chart/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/githublitao%2FHTMLTestRunner_Chart/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/githublitao","download_url":"https://codeload.github.com/githublitao/HTMLTestRunner_Chart/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":230367928,"owners_count":18215338,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":[],"created_at":"2024-11-14T04:27:22.771Z","updated_at":"2024-12-19T03:08:42.296Z","avatar_url":"https://github.com/githublitao.png","language":"Python","funding_links":[],"categories":[],"sub_categories":[],"readme":"# HTMLTestRunner_Chart 基于unittest的测试报告，使用详情见demo\n参考链接：\u003cbr\u003e\nhttp://tungwaiyip.info/software/HTMLTestRunner.html\u003cbr\u003e\nhttps://github.com/GoverSky/HTMLTestRunner_cn\u003cbr\u003e\n### 优化报告内容\n1. 测试报告中文显示，优化一些断言失败正文乱码问题\u003cbr\u003e\n2. 新增错误和失败截图，展示到html报告里\u003cbr\u003e\n3. 增加饼图统计\u003cbr\u003e\n4. 失败后重试功能\u003cbr\u003e\n5. 保存近10次测试结果，并通过柱状图展示\u003cbr\u003e\n6. 切换测试日期，展示历史测试结果\u003cbr\u003e\n兼容python2.x 和3.x\n### 注意：\n1. 在是python3.x 中，如果在这里setUp里初始化driver ，因为3.x版本 unittest 运行机制不同，会导致用力失败时截图失败，目前只有采用捕获异常来截图，或者在setUpClass里初始化driver\u003cbr\u003e\n2. driver初始化变量名必须命名为driver\n### 报告首页：\n![报告截图](https://github.com/githublitao/HTMLTestRunner_Chart/blob/master/img/%E9%A6%96%E9%A1%B51.png)\u003cbr\u003e\n### 用例截图：\n![用例截图](https://github.com/githublitao/HTMLTestRunner_Chart/blob/master/img/%E6%98%BE%E7%A4%BA%E6%88%AA%E5%9B%BE1.png)\u003cbr\u003e\n### 失败饼图：\n![失败饼图](https://github.com/githublitao/HTMLTestRunner_Chart/blob/master/img/%E9%A5%BC%E5%9B%BE1.png)\u003cbr\u003e\n### 历史走势：\n![历史走势](https://github.com/githublitao/HTMLTestRunner_Chart/blob/master/img/%E8%B5%B0%E5%8A%BF%E5%9B%BE1.png)\u003cbr\u003e\n### 微信打赏：\n![微信打赏](https://github.com/githublitao/api_automation_test/blob/master/img/%E6%94%B6%E6%AC%BE%E7%A0%81.png)\u003cbr\u003e\n\u003cbr\u003e\n### 失败重试：\n1. 生成报告的参数里面加了一个参数retry=1,这个表示用例失败后，会重新跑一次。\u003cbr\u003e\n```python\nif __name__ == '__main__':\n    suite = unittest.TestLoader().loadTestsFromTestCase(case_01)\n    runner = HTMLTestRunner(\n        title=\"带截图，饼图，折线图，历史结果查看的测试报告\",\n        description=\"\",\n        stream=open(\"./demo.html\", \"wb\"),\n        verbosity=2,\n        retry=0,\n        save_last_try=True)\n    runner.run(suite)\n```\n### 保存测试结果到json文件：\n```python\n    def mkdir_json(self):\n        is_exists = os.path.exists(self.path)\n        # 判断结果\n        if not is_exists:\n            try:\n                # 如果不存在则创建目录\n                # 创建目录操作函数\n                with open(self.path, \"w+\") as f:\n                    f.write(\"var data = []\")\n                return True\n            except Exception as e:\n                print(e)\n                return False\n        else:\n            return True\n\n    def Write(self, title, heading, desc, data):\n        try:\n            with open(self.path, \"r+\") as f:\n                all_data = f.read().split(\" = \", 1)\n                data_json = all_data[1]\n                data_json = eval(data_json)\n                if len(data_json) \u003e= 10:\n                    del data_json[0]\n                description = dict()\n                description[\"startTime\"] = heading[0][1]\n                description[\"duration\"] = heading[1][1]\n                if PY3K:\n                    description[\"title\"] = title\n                    description[\"status\"] = heading[2][1]\n                    description[\"desc\"] = desc\n                    description[\"data\"] = data\n                    status = heading[2][1].split(\" \")\n                    for j in range(0, len(status)):\n                        if status[j] == \"通过\":\n                            description[\"success\"] = str(status[j + 1])\n                        if status[j] == \"失败\":\n                            description[\"fail\"] = str(status[j + 1])\n                        if status[j] == \"错误\":\n                            description[\"error\"] = str(status[j + 1])\n                else:\n                    description[\"title\"] = title.encode(\"gbk\")\n                    description[\"status\"] = heading[2][1].encode(\"gbk\")\n                    description[\"desc\"] = desc.encode(\"gbk\")\n                    description[\"data\"] = data.encode(\"gbk\")\n                    status = heading[2][1].split(\" \")\n                    for j in range(0, len(status)):\n                        if status[j] == u\"通过\":\n                            description[\"success\"] = str(status[j + 1])\n                        if status[j] == u\"失败\":\n                            description[\"fail\"] = str(status[j + 1])\n                        if status[j] == u\"错误\":\n                            description[\"error\"] = str(status[j + 1])\n                data_json.append(description)\n                data_json = str(data_json)\n                f.seek(0)\n                f.truncate()\n                f.write(str(\"var data = \" + data_json))\n        except IndexError:\n            sys.stderr.write(\"JSON初始化内容有误! 初始化内容’var data = []‘\")\n```\n### 错误/失败截图，修改addError和addFail函数：\n```python\n    def addFailure(self, test, err):\n        self.failure_count += 1\n        self.status = 1\n        TestResult.addFailure(self, test, err)\n        _, _exc_str = self.failures[-1]\n        output = self.complete_output()\n        self.result.append((1, test, output, _exc_str))\n        if not getattr(test, \"driver\",\"\"):\n            pass\n        else:\n            try:\n                driver = getattr(test, \"driver\")\n                test.imgs.append(driver.get_screenshot_as_base64())\n            except Exception as e:\n                pass\n        if self.verbosity \u003e 1:\n            sys.stderr.write('F  ')\n            sys.stderr.write(str(test))\n            sys.stderr.write('\\n')\n        else:\n            sys.stderr.write('F')\n```\n### 错误重试，修改stopTest函数：\n ```python\n     def stopTest(self, test):\n        # Usually one of addSuccess, addError or addFailure would have been called.\n        # But there are some path in unittest that would bypass this.\n        # We must disconnect stdout in stopTest(), which is guaranteed to be called.\n        if self.retry:\n            if self.status == 1:\n                self.trys += 1\n                if self.trys \u003c= self.retry:\n                    if self.save_last_try:\n                        t = self.result.pop(-1)\n                        if t[0]==1:\n                            self.failure_count-=1\n                        else:\n                            self.error_count -= 1\n                    test=copy.copy(test)\n                    sys.stderr.write(\"Retesting... \")\n                    sys.stderr.write(str(test))\n                    sys.stderr.write('..%d \\n' % self.trys)\n                    doc = test._testMethodDoc or ''\n                    if doc.find('_retry')!=-1:\n                        doc = doc[:doc.find('_retry')]\n                    desc =\"%s_retry:%d\" %(doc, self.trys)\n                    if not PY3K:\n                        if isinstance(desc, str):\n                            desc = desc.decode(\"utf-8\")\n                    test._testMethodDoc = desc\n                    test(self)\n                else:\n                    self.status = 0\n                    self.trys = 0\n        self.complete_output()\n ```\n### HTML模板导入JSON历史结果，如果JSON出现错误，则历史结果和走势图错误：\n ```html\n \u003chead\u003e\n    \u003ctitle\u003e%(title)s\u003c/title\u003e\n    \u003cmeta name=\"generator\" content=\"%(generator)s\"/\u003e\n    \u003cmeta http-equiv=\"Content-Type\" content=\"text/html;charset=utf-8\"/\u003e\n    \u003cscript type=\"text/javascript\" src=\"%(jsonpath)s\" charset=\"gbk\"\u003e\u003c/script\u003e\n    \u003clink href=\"http://cdn.bootcss.com/bootstrap/3.3.0/css/bootstrap.min.css\" rel=\"stylesheet\"\u003e\n    \u003cscript src=\"https://cdn.bootcss.com/echarts/3.8.5/echarts.common.min.js\"\u003e\u003c/script\u003e\n    \u003c!-- \u003cscript type=\"text/javascript\" src=\"js/echarts.common.min.js\"\u003e\u003c/script\u003e --\u003e\n    \n    %(stylesheet)s\n    \n\u003c/head\u003e\n ```\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fgithublitao%2Fhtmltestrunner_chart","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fgithublitao%2Fhtmltestrunner_chart","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fgithublitao%2Fhtmltestrunner_chart/lists"}