Repository: GoverSky/HTMLTestRunner_cn Branch: master Commit: 1792ed420e2a Files: 9 Total size: 1.3 MB Directory structure: gitextract_ytdbk0_c/ ├── .gitattributes ├── .gitignore ├── HTMLTestRunner_cn.py ├── README.md ├── changelog.md ├── sample_test_report.html ├── sample_test_report_appium.html ├── test_screenshot_appium.py └── test_screenshot_selenium.py ================================================ FILE CONTENTS ================================================ ================================================ FILE: .gitattributes ================================================ *.py linguist-language=Python *.html linguist-language=Python ================================================ FILE: .gitignore ================================================ # Created by .ignore support plugin (hsz.mobi) ### Python template # Byte-compiled / optimized / DLL files __pycache__/ *.py[cod] *$py.class # C extensions *.so # Distribution / packaging .Python build/ develop-eggs/ dist/ downloads/ eggs/ .eggs/ lib/ lib64/ parts/ sdist/ var/ wheels/ pip-wheel-metadata/ share/python-wheels/ *.egg-info/ .installed.cfg *.egg MANIFEST # PyInstaller # Usually these files are written by a python script from a template # before PyInstaller builds the exe, so as to inject date/other infos into it. *.manifest *.spec # Installer logs pip-log.txt pip-delete-this-directory.txt # Unit test / coverage reports htmlcov/ .tox/ .nox/ .coverage .coverage.* .cache nosetests.xml coverage.xml *.cover .hypothesis/ .pytest_cache/ # Translations *.mo *.pot # Django stuff: *.log local_settings.py db.sqlite3 # Flask stuff: instance/ .webassets-cache # Scrapy stuff: .scrapy # Sphinx documentation docs/_build/ # PyBuilder target/ # Jupyter Notebook .ipynb_checkpoints # IPython profile_default/ ipython_config.py # pyenv .python-version # pipenv # According to pypa/pipenv#598, it is recommended to include Pipfile.lock in version control. # However, in case of collaboration, if having platform-specific dependencies or dependencies # having no cross-platform support, pipenv may install dependencies that don't work, or not # install all needed dependencies. #Pipfile.lock # celery beat schedule file celerybeat-schedule # SageMath parsed files *.sage.py # Environments .env .venv env/ venv/ ENV/ env.bak/ venv.bak/ # Spyder project settings .spyderproject .spyproject # Rope project settings .ropeproject # mkdocs documentation /site # mypy .mypy_cache/ .dmypy.json dmypy.json # Pyre type checker .pyre/ .idea/* /.idea/* Untitled.ipynb /.ipynb_checkpoints/ ================================================ FILE: HTMLTestRunner_cn.py ================================================ #-*- coding: utf-8 -*- """ A TestRunner for use with the Python unit testing framework. It generates a HTML report to show the result at a glance. The simplest way to use this is to invoke its main method. E.g. import unittest import HTMLTestRunner ... define your tests ... if __name__ == '__main__': HTMLTestRunner.main() For more customization options, instantiates a HTMLTestRunner object. HTMLTestRunner is a counterpart to unittest's TextTestRunner. E.g. # output to a file fp = file('my_report.html', 'wb') runner = HTMLTestRunner.HTMLTestRunner( stream=fp, title='My unit test', description='This demonstrates the report output by HTMLTestRunner.' ) # Use an external stylesheet. # See the Template_mixin class for more customizable options runner.STYLESHEET_TMPL = '' # run the test runner.run(my_test_suite) ------------------------------------------------------------------------ Copyright (c) 2004-2007, Wai Yip Tung All rights reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: * Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. * Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. * Neither the name Wai Yip Tung nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. """ # URL: http://tungwaiyip.info/software/HTMLTestRunner.html __author__ = "Wai Yip Tung" __version__ = "0.8.3" """ Change History Version 0.8.4 by GoverSky * Add sopport for 3.x * Add piechart for resultpiechart * Add Screenshot for selenium_case test * Add Retry on failed Version 0.8.3 * Prevent crash on class or module-level exceptions (Darren Wurf). Version 0.8.2 * Show output inline instead of popup window (Viorel Lupu). Version in 0.8.1 * Validated XHTML (Wolfgang Borgert). * Added description of test classes and test cases. Version in 0.8.0 * Define Template_mixin class for customization. * Workaround a IE 6 bug that it does not treat %(heading)s
%(report)s %(ending)s """ # variables: (title, generator, stylesheet, heading, report, ending) # ------------------------------------------------------------------------ # Stylesheet # # alternatively use a for external style sheet, e.g. # STYLESHEET_TMPL = """ """ # ------------------------------------------------------------------------ # Heading # HEADING_TMPL = """

%(title)s

%(parameters)s

%(description)s

""" # variables: (title, parameters, description) HEADING_ATTRIBUTE_TMPL = """

%(name)s: %(value)s

""" # variables: (name, value) # ------------------------------------------------------------------------ # Report # REPORT_TMPL = """
概要[%(Pass_p).2f%%] 通过[%(Pass)s] 失败[%(fail)s] 错误[%(error)s] 跳过[%(skip)s] 所有[%(total)s]
%(test_list)s
测试组/测试用例 总数 通过 失败 错误 视图 错误截图
统计 %(count)s %(Pass)s %(fail)s %(error)s    
""" # variables: (test_list, count, Pass, fail, error) REPORT_CLASS_TMPL = r""" %(desc)s %(count)s %(Pass)s %(fail)s %(error)s 详情   """ # variables: (style, desc, count, Pass, fail, error, cid) REPORT_TEST_WITH_OUTPUT_TMPL = r"""
%(desc)s
%(status)s %(img)s """ # variables: (tid, Class, style, desc, status,img) REPORT_TEST_NO_OUTPUT_TMPL = r"""
%(desc)s
%(status)s %(img)s """ # variables: (tid, Class, style, desc, status,img) REPORT_TEST_OUTPUT_TMPL = r""" %(id)s: %(output)s """ # variables: (id, output) IMG_TMPL = r""" 显示截图 """ # ------------------------------------------------------------------------ # ENDING # ENDING_TMPL = """
 
""" # -------------------- The end of the Template class ------------------- def __getattribute__(self, item): value = object.__getattribute__(self, item) if PY3K: return value else: if isinstance(value, str): return value.decode("utf-8") else: return value TestResult = unittest.TestResult class _TestResult(TestResult): # note: _TestResult is a pure representation of results. # It lacks the output and reporting ability compares to unittest._TextTestResult. shouldStop=False def __init__(self, verbosity=1, retry=0,save_last_try=False): TestResult.__init__(self) self.stdout0 = None self.stderr0 = None self.success_count = 0 self.failure_count = 0 self.error_count = 0 self.skip_count = 0 self.verbosity = verbosity # result is a list of result in 4 tuple # ( # result code (0: success; 1: fail; 2: error;3:skip), # TestCase object, # Test output (byte string), # stack trace, # ) self.result = [] self.retry = retry self.trys = 0 self.status = 0 self.save_last_try = save_last_try self.outputBuffer = StringIO.StringIO() def startTest(self, test): # test.imgs = [] test.imgs = getattr(test, "imgs", []) # TestResult.startTest(self, test) self.outputBuffer.seek(0) self.outputBuffer.truncate() stdout_redirector.fp = self.outputBuffer stderr_redirector.fp = self.outputBuffer self.stdout0 = sys.stdout self.stderr0 = sys.stderr sys.stdout = stdout_redirector sys.stderr = stderr_redirector def complete_output(self): """ Disconnect output redirection and return buffer. Safe to call multiple times. """ if self.stdout0: sys.stdout = self.stdout0 sys.stderr = self.stderr0 self.stdout0 = None self.stderr0 = None return self.outputBuffer.getvalue() def stopTest(self, test): # Usually one of addSuccess, addError or addFailure would have been called. # But there are some path in unittest that would bypass this. # We must disconnect stdout in stopTest(), which is guaranteed to be called. if self.retry and self.retry>=1: if self.status == 1: self.trys += 1 if self.trys <= self.retry: if self.save_last_try: t = self.result.pop(-1) if t[0]==1: self.failure_count -=1 else: self.error_count -= 1 test=copy.copy(test) sys.stderr.write("Retesting... ") sys.stderr.write(str(test)) sys.stderr.write('..%d \n' % self.trys) doc = getattr(test,'_testMethodDoc',u"") or u'' if doc.find('_retry')!=-1: doc = doc[:doc.find('_retry')] desc ="%s_retry:%d" %(doc, self.trys) if not PY3K: if isinstance(desc, str): desc = desc.decode("utf-8") test._testMethodDoc = desc test(self) else: self.status = 0 self.trys = 0 self.complete_output() def addSuccess(self, test): self.success_count += 1 self.status = 0 TestResult.addSuccess(self, test) output = self.complete_output() self.result.append((0, test, output, '')) if self.verbosity > 1: sys.stderr.write('P ') sys.stderr.write(str(test)) sys.stderr.write('\n') else: sys.stderr.write('P') def addFailure(self, test, err): self.failure_count += 1 self.status = 1 TestResult.addFailure(self, test, err) _, _exc_str = self.failures[-1] output = self.complete_output() self.result.append((1, test, output, _exc_str)) if not getattr(test, "driver",""): pass else: try: driver = getattr(test, "driver") test.imgs.append(driver.get_screenshot_as_base64()) except Exception as e: pass if self.verbosity > 1: sys.stderr.write('F ') sys.stderr.write(str(test)) sys.stderr.write('\n') else: sys.stderr.write('F') def addError(self, test, err): self.error_count += 1 self.status = 1 TestResult.addError(self, test, err) _, _exc_str = self.errors[-1] output = self.complete_output() self.result.append((2, test, output, _exc_str)) if not getattr(test, "driver",""): pass else: try: driver = getattr(test, "driver") test.imgs.append(driver.get_screenshot_as_base64()) except Exception: pass if self.verbosity > 1: sys.stderr.write('E ') sys.stderr.write(str(test)) sys.stderr.write('\n') else: sys.stderr.write('E') def addSkip(self, test, reason): self.skip_count += 1 self.status = 0 TestResult.addSkip(self, test,reason) output = self.complete_output() self.result.append((3, test, output, reason)) if self.verbosity > 1: sys.stderr.write('K') sys.stderr.write(str(test)) sys.stderr.write('\n') else: sys.stderr.write('K') class HTMLTestRunner(Template_mixin): def __init__(self, stream=sys.stdout, verbosity=1, title=None, description=None,is_thread=False, retry=0,save_last_try=True): self.stream = stream self.retry = retry self.is_thread=is_thread self.threads= 5 self.save_last_try=save_last_try self.verbosity = verbosity self.run_times=0 if title is None: self.title = self.DEFAULT_TITLE else: self.title = title if description is None: self.description = self.DEFAULT_DESCRIPTION else: self.description = description def run(self, test): "Run the given test case or test suite." self.startTime = datetime.datetime.now() result = _TestResult(self.verbosity, self.retry, self.save_last_try) test(result) self.stopTime = datetime.datetime.now() self.run_times+=1 self.generateReport(result) if PY3K: # for python3 # print('\nTime Elapsed: %s' % (self.stopTime - self.startTime),file=sys.stderr) output = '\nTime Elapsed: %s' % (self.stopTime - self.startTime) sys.stderr.write(output) else: # for python2 print >> sys.stderr, '\nTime Elapsed: %s' % (self.stopTime - self.startTime) return result def sortResult(self, result_list): # unittest does not seems to run in any particular order. # Here at least we want to group them together by class. rmap = {} classes = [] for n, t, o, e in result_list: cls = t.__class__ if not cls in rmap: rmap[cls] = [] classes.append(cls) rmap[cls].append((n, t, o, e)) for cls in classes: rmap[cls].sort(key=cmp_to_key(lambda a,b:1 if a[1].id()>b[1].id() else ( 1 if a[1].id()==b[1].id() else -1))) r = [(cls, rmap[cls]) for cls in classes] # name = t.id().split('.')[-1] r.sort(key=cmp_to_key(lambda a, b: 1 if a[0].__name__ > b[0].__name__ else -1)) return r def getReportAttributes(self, result): """ Return report attributes as a list of (name, value). Override this to add custom attributes. """ startTime = str(self.startTime)[:19] duration = str(self.stopTime - self.startTime) status = [] if result.success_count: status.append(u'Pass:%s' % result.success_count) if result.failure_count: status.append(u'Failure:%s' % result.failure_count) if result.error_count: status.append(u'Error:%s' % result.error_count) if result.skip_count: status.append(u'Skip:%s' % result.skip_count) total = result.success_count+result.failure_count+result.error_count + result.skip_count if total>0: passed = result.success_count*1.000/total*100 else: passed =0.0 status.append(u'通过率:%.1f%%' % passed) if status: status = u' '.join(status) else: status = 'none' return [ (u'开始时间', startTime), (u'耗时', duration), (u'状态', status), ] def generateReport(self, result): report_attrs = self.getReportAttributes(result) generator = 'HTMLTestRunner %s' % __version__ stylesheet = self._generate_stylesheet() heading = self._generate_heading(report_attrs) report = self._generate_report(result) ending = self._generate_ending() output = self.HTML_TMPL % dict( title=saxutils.escape(self.title), generator=generator, stylesheet=stylesheet, heading=heading, report=report, ending=ending, channel=self.run_times, ) if PY3K: self.stream.write(output.encode()) else: self.stream.write(output.encode('utf8')) def _generate_stylesheet(self): return self.STYLESHEET_TMPL def _generate_heading(self, report_attrs): a_lines = [] for name, value in report_attrs: line = self.HEADING_ATTRIBUTE_TMPL % dict( name=name, value=value, ) a_lines.append(line) heading = self.HEADING_TMPL % dict( title=saxutils.escape(self.title), parameters=''.join(a_lines), description=saxutils.escape(self.description), ) return heading def _generate_report(self, result): rows = [] sortedResult = self.sortResult(result.result) for cid, (cls, cls_results) in enumerate(sortedResult): # subtotal for a class np = nf = ne = ns = 0 for n, t, o, e in cls_results: if n == 0: np += 1 elif n == 1: nf += 1 elif n==2: ne += 1 else: ns +=1 # format class description if cls.__module__ == "__main__": name = cls.__name__ else: name = "%s.%s" % (cls.__module__, cls.__name__) doc = cls.__doc__ and cls.__doc__.split("\n")[0] or "" desc = doc and '%s: %s' % (name, doc) or name if not PY3K: if isinstance(desc, str): desc = desc.decode("utf-8") row = self.REPORT_CLASS_TMPL % dict( style=ne > 0 and 'errorClass' or nf > 0 and 'failClass' or 'passClass', desc=desc, count=np + nf + ne, Pass=np, fail=nf, error=ne, cid='c%s.%s' % (self.run_times,cid + 1), ) rows.append(row) for tid, (n, t, o, e) in enumerate(cls_results): self._generate_report_test(rows, cid, tid, n, t, o, e) total = result.success_count + result.failure_count + result.error_count +result.skip_count report = self.REPORT_TMPL % dict( test_list=u''.join(rows), count=str(total), Pass=str(result.success_count), Pass_p=result.success_count*1.00/total*100 if total else 0.0, fail=str(result.failure_count), error=str(result.error_count), skip=str(result.skip_count), total=str(total), channel=str(self.run_times), ) return report def _generate_report_test(self, rows, cid, tid, n, t, o, e): # e.g. 'pt1.1', 'ft1.1', etc has_output = bool(o or e) if n==0: tmp="p" elif n==1: tmp="f" elif n==2: tmp = "e" else: tmp = "s" tid = tmp + 't%d.%d.%d' % (self.run_times,cid + 1, tid + 1) name = t.id().split('.')[-1] if self.verbosity > 1: doc = getattr(t,'_testMethodDoc',"") or '' else: doc = "" desc = doc and ('%s: %s' % (name, doc)) or name if not PY3K: if isinstance(desc, str): desc = desc.decode("utf-8") tmpl = has_output and self.REPORT_TEST_WITH_OUTPUT_TMPL or self.REPORT_TEST_NO_OUTPUT_TMPL # o and e should be byte string because they are collected from stdout and stderr? if isinstance(o, str): # uo = unicode(o.encode('string_escape')) if PY3K: uo = o else: uo = o.decode('utf-8', 'ignore') else: uo = o if isinstance(e, str): # ue = unicode(e.encode('string_escape')) if PY3K: ue = e elif e.find("Error") != -1 or e.find("Exception") != -1: es = e.decode('utf-8', 'ignore').split('\n') try: if es[-2].find("\\u") != -1 or es[-2].find('"\\u') != -1: es[-2] = es[-2].decode('unicode_escape') except Exception: pass ue = u"\n".join(es) else: ue = e.decode('utf-8', 'ignore') else: ue = e script = self.REPORT_TEST_OUTPUT_TMPL % dict( id=tid, output=saxutils.escape(uo + ue), ) if getattr(t,'imgs',[]): # 判断截图列表,如果有则追加 tmp = u"" for i, img in enumerate(t.imgs): if i==0: tmp+=""" \n""" % img else: tmp+=""" \n""" % img imgs = self.IMG_TMPL % dict(imgs=tmp) else: imgs = u"""无截图""" row = tmpl % dict( tid=tid, Class=(n == 0 and 'hiddenRow' or 'none'), style=n == 2 and 'errorCase' or (n == 1 and 'failCase' or 'passCase'), desc=desc, script=script, status=self.STATUS[n], img=imgs, ) rows.append(row) if not has_output: return def _generate_ending(self): return self.ENDING_TMPL ############################################################################## # Facilities for running tests from the command line ############################################################################## # Note: Reuse unittest.TestProgram to launch test. In the future we may # build our own launcher to support more specific command line # parameters like test title, CSS, etc. class TestProgram(unittest.TestProgram): """ A variation of the unittest.TestProgram. Please refer to the base class for command line parameters. """ def runTests(self): # Pick HTMLTestRunner as the default test runner. # base class's testRunner parameter is not useful because it means # we have to instantiate HTMLTestRunner before we know self.verbosity. if self.testRunner is None: self.testRunner = HTMLTestRunner(verbosity=self.verbosity) unittest.TestProgram.runTests(self) main = TestProgram ############################################################################## # Executing this module from the command line ############################################################################## if __name__ == "__main__": main(module=None) ================================================ FILE: README.md ================================================ # HTMLTestRunner 汉化版 在原版的基础上进行扩展和改造 # 当年改造初衷 + 方便自己做汉化报告生成 + 对自己积累知识的检验 + 挑战下单文件报告都能做出什么花样 近两年不怎么搞UI自动化了,项目就一直没怎么更新(pytest香啊😅) # todo + 多线程/多进程执行用例(数据统计逻辑要重新设计,还有兼容性问题😑) + UI 美化 (通过CDN集成一些成熟的js库~然后加5毛钱特效😜) + 与ddt的集成(目测基本就把源码收进来😏) # 报告汉化,错误日志 ![](./img/1.png) # selenium/appium 截图 截图功能根据测试结果,当结果为fail或error时自动截图
截图方法在_TestResult 的测试结果收集中,报告使用的截图全部保存为base64编码,避免了报告图片附件的问题,可以根据自己使用的框架不同自行调整,selenium 使用的是get_screenshot_as_base64 方法获取页面截图的base64编码
![](./img/2.png) 因为要提取用例中的driver变量获取webdriver对象,所以要实现截图功能必须定义在用例中定义webdriver 为driver ```python def setUp(self): self.imgs=[] # (可选)初始化截图列表 self.driver = webdriver.Chrome() ``` 或者 ```python @classmethod def setUpClass(cls): cls.driver = webdriver.Chrome() ``` 也可以在测试过程中某一步骤自定义添加截图,比如
![](./img/3.png)
生成报告后会统一进行展示
**Selenium截图轮播效果**
![](./img/4.gif)
**Appium效果轮播截图**
![](./img/5.gif) # 用例失败重试 根据unittest的运行机制,在stopTest 中判断测试结果,如果失败或出错status为1,判断是否需要重试;
![](./img/5.png) 在实例化HTMLTestRunner 对象时追加参数,retry,指定重试次数,如果save_last_try 为True ,一个用例仅显示最后一次测试的结果。 ```python HTMLTestRunner(title="带截图的测试报告", description="小试牛刀", stream=open("sample_test_report.html", "wb"), verbosity=2, retry=2, save_last_try=True) ``` ![](./img/6.png) 如果save_last_try 为False,则显示所有重试的结果。 ```python HTMLTestRunner(title="带截图的测试报告", description="小试牛刀", stream=open("sample_test_report.html", "wb"), verbosity=2, retry=2, save_last_try=False) ``` ![](./img/7.png) 运行中输出效果如下:
![](./img/8.png) `注意:在python3 中因为unittest运行机制变动,在使用setUp/tearDown中初始化/退出driver时,会出现用例执行失败没有截图的问题,所以推荐使用样例中setUpClass/tearDownClass的用法` ================================================ FILE: changelog.md ================================================ # changelog + 20170925 - 测试报告完全汉化,包括错误日志的中文处理 - 针对selenium UI测试增加失败自动截图功能,截图自动轮播 - 增加失败自动重试功能 - 增加饼图统计 - 同时兼容python2.x 和3.x + 20180402 - 表格样式优化 - 修复部分bug - 增加截图组,可展示多张截图,首次打开自动播放 - 增加仅展示最后一次运行结果,多次重试时,每个测试用例仅展示一次 + 20181213 - 增加分类标签、通过率等,优化样式 - 修复部分框架在SetUP中失败导致测试中断的问题导致 ErrorHandle的问题 - 修复部分编码Bug - 优化运行逻辑 - 对js代码优化,修复部分多次运行run导致结果异常的bug + 20200427 - 修复页面小错误 (fzk27) + 20200508 - 开放跳过测试的统计,完善饼图统计 ================================================ FILE: sample_test_report.html ================================================ 带截图的测试报告

带截图的测试报告

开始时间: 2020-05-07 16:50:04

耗时: 0:01:36.860445

状态: Pass:4 Failure:2 Error:1 Skip:1 通过率:50.0%

小试牛刀

概要[50.00%] 通过[4] 失败[2] 错误[1] 跳过[1] 所有[8]
测试组/测试用例 总数 通过 失败 错误 视图 错误截图
case_01 4 1 2 1 详情  
test_case1: 百度搜索 呵呵呵呵
通过 无截图
test_case2: 搜狗首页_retry:2
失败 显示截图
test_case3: QQ邮箱_retry:2
失败 显示截图
test_case4: 淘宝_retry:2
错误 显示截图
case_02 3 3 0 0 详情  
test_case1: 百度搜索 呵呵呵呵
通过 无截图
test_case2: 搜狗首页
跳过 无截图
test_case3: QQ邮箱
通过 无截图
test_case4: 淘宝
通过 无截图
统计 8 4 2 1    
 
================================================ FILE: sample_test_report_appium.html ================================================ 带截图的测试报告

带截图的测试报告

开始时间: 2018-12-13 11:34:08

耗时: 0:00:36.348000

状态: Pass:1 通过率:100.0%

小试牛刀

概要[100.00%] 通过[1] 失败[0] 错误[0] 所有[1]
测试组/测试用例 总数 通过 失败 错误 视图 错误截图
case_01 1 1 0 0 详情  
test_case1: 手机QQ截图
通过 显示截图
统计 1 1 0 0    
 
================================================ FILE: test_screenshot_appium.py ================================================ # -*- coding: utf-8 -*- # @Time : 2017/9/6 11:26 # @File : aaa.py # @Author : 守望@天空~ """HTMLTestRunner 截图版示例 appium版""" from appium import webdriver import unittest from HTMLTestRunner_cn import HTMLTestRunner class case_01(unittest.TestCase): @classmethod def setUpClass(cls): desired_caps = {} desired_caps['platformName'] = 'Android' desired_caps['platformVersion'] = '4.4.2' desired_caps['deviceName'] = 'Android Emulator' desired_caps['app'] = 'com.tencent.mobileqq' desired_caps['appActivity'] = 'com.tencent.mobileqq.activity.SplashActivity' cls.driver = webdriver.Remote('http://localhost:4723/wd/hub', desired_caps) @classmethod def tearDownClass(cls): cls.driver.quit() def add_img(self): # 在是python3.x 中,如果在这里初始化driver ,因为3.x版本 unittest 运行机制不同,会导致用力失败时截图失败 self.imgs.append(self.driver.get_screenshot_as_base64()) return True def setUp(self): self.imgs = [] self.addCleanup(self.cleanup) def cleanup(self): pass def test_case1(self): """ 手机QQ截图""" self.add_img() self.add_img() self.add_img() self.add_img() self.add_img() if __name__ == "__main__": suite = unittest.TestLoader().loadTestsFromTestCase(case_01) runer = HTMLTestRunner(title="带截图的测试报告", description="小试牛刀", stream=open("sample_test_report_appium.html", "wb"), verbosity=2, retry=1, save_last_try=True) runer.run(suite) ================================================ FILE: test_screenshot_selenium.py ================================================ # -*- coding: utf-8 -*- # @Time : 2017/9/6 11:26 # @File : aaa.py # @Author : 守望@天空~ """HTMLTestRunner 截图版示例 selenium 版""" from selenium import webdriver import unittest import time from HTMLTestRunner_cn import HTMLTestRunner import sys class case_01(unittest.TestCase): """ def setUp(cls): cls.driver = webdriver.Chrome() def tearDown(cls): cls.driver.quit() """ @classmethod def setUpClass(cls): cls.driver = webdriver.Chrome() @classmethod def tearDownClass(cls): cls.driver.quit() def add_img(self): # self.imgs.append(self.driver.get_screenshot_as_base64()) return True def setUp(self): # 在python3.x 中,如果在这里初始化driver ,因为3.x版本 unittest 运行机制不同,会导致用例失败后截图失败 self.imgs = [] self.addCleanup(self.cleanup) def cleanup(self): pass def test_case1(self): """ 百度搜索 呵呵呵呵 """ print("本次校验没过?") print ("超级长"*66) self.driver.get("https://www.baidu.com") self.add_img() self.driver.find_element_by_id('kw').send_keys(u'百度一下') self.add_img() self.driver.find_element_by_id('su').click() time.sleep(1) self.add_img() def test_case2(self): """搜狗首页""" self.driver.get("http://www.sogou.com") print("本次校验没过?") self.assertTrue(False,"这是相当的睿智了") def test_case3(self): """ QQ邮箱""" self.driver.get("https://mail.qq.com") # self.imgs.append(self.driver.get_screenshot_as_base64()) print("没法打印?") self.assertIn(u"中文", u'中华','小当家?') def test_case4(self): u""" 淘宝""" self.driver.get("http://www.taobao.com/") raise Exception self.add_img() self.assertTrue(True) class case_02(unittest.TestCase): """ def setUp(cls): cls.driver = webdriver.Chrome() def tearDown(cls): cls.driver.quit() """ @classmethod def setUpClass(cls): cls.driver = webdriver.Chrome() @classmethod def tearDownClass(cls): cls.driver.quit() def add_img(self): # self.imgs.append(self.driver.get_screenshot_as_base64()) return True def setUp(self): # 在是python3.x 中,如果在这里初始化driver ,因为3.x版本 unittest 运行机制不同,会导致用力失败时截图失败 self.imgs = [] self.addCleanup(self.cleanup) def cleanup(self): pass def test_case1(self): """ 百度搜索 呵呵呵呵 """ print("校验了一下") self.driver.get("https://www.baidu.com") self.add_img() self.driver.find_element_by_id('kw').send_keys(u'百度一下') self.add_img() self.driver.find_element_by_id('su').click() time.sleep(1) self.add_img() @unittest.skip('跳过') def test_case2(self): """搜狗首页""" self.driver.get("http://www.sogou.com") print("本次校验没过?") self.assertTrue(False,"这是相当的睿智了") def test_case3(self): """ QQ邮箱""" self.driver.get("https://mail.qq.com") # self.imgs.append(self.driver.get_screenshot_as_base64()) print("没法打印?") self.assertIn(u"中文", u'中文') def test_case4(self): u""" 淘宝""" self.driver.get("http://www.taobao.com/") self.add_img() self.assertTrue(True) if __name__ == "__main__": from unittest import TestResult suite1 = unittest.TestLoader().loadTestsFromTestCase(case_01) suite2 = unittest.TestLoader().loadTestsFromTestCase(case_02) suites = unittest.TestSuite() suites.addTests([suite1,suite2]) runer = HTMLTestRunner(title="带截图的测试报告", description="小试牛刀", stream=open("sample_test_report.html", "wb"), verbosity=2, retry=2, save_last_try=True) runer.run(suites)