diff --git a/.gitattributes b/.gitattributes new file mode 100644 index 0000000..0acd5ed --- /dev/null +++ b/.gitattributes @@ -0,0 +1,2 @@ +*.py linguist-language=Python +*.html linguist-language=Python \ No newline at end of file diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..39a1309 --- /dev/null +++ b/.gitignore @@ -0,0 +1,129 @@ +# 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/ diff --git a/HTMLTestRunner.py b/HTMLTestRunner.py deleted file mode 100644 index 8d60600..0000000 --- a/HTMLTestRunner.py +++ /dev/null @@ -1,835 +0,0 @@ -""" -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.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 = """ -

Show -Summary -Failed -All -

- -------- - - - - - - - - -%(test_list)s - - - - - - - - -
Test Group/Test caseCountPassFailErrorView
Total%(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 - Detail - -""" # variables: (style, desc, count, Pass, fail, error, cid) - - - REPORT_TEST_WITH_OUTPUT_TMPL = r""" - -
%(desc)s
- - - - - %(status)s - - - - - - -""" # variables: (tid, Class, style, desc, status) - - - REPORT_TEST_NO_OUTPUT_TMPL = r""" - -
%(desc)s
- %(status)s - -""" # variables: (tid, Class, style, desc, status) - - - REPORT_TEST_OUTPUT_TMPL = r""" -%(id)s: %(output)s -""" # variables: (id, output) - - - - # ------------------------------------------------------------------------ - # ENDING - # - - ENDING_TMPL = """
 
""" - -# -------------------- The end of the Template class ------------------- - - -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. - - def __init__(self, verbosity=1): - TestResult.__init__(self) - self.outputBuffer = StringIO.StringIO() - self.stdout0 = None - self.stderr0 = None - self.success_count = 0 - self.failure_count = 0 - self.error_count = 0 - self.verbosity = verbosity - - # result is a list of result in 4 tuple - # ( - # result code (0: success; 1: fail; 2: error), - # TestCase object, - # Test output (byte string), - # stack trace, - # ) - self.result = [] - - - def startTest(self, test): - TestResult.startTest(self, test) - # just one buffer for both stdout and stderr - 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. - self.complete_output() - - - def addSuccess(self, test): - self.success_count += 1 - TestResult.addSuccess(self, test) - output = self.complete_output() - self.result.append((0, test, output, '')) - if self.verbosity > 1: - sys.stderr.write('ok ') - sys.stderr.write(str(test)) - sys.stderr.write('\n') - else: - sys.stderr.write('.') - - def addError(self, test, err): - self.error_count += 1 - TestResult.addError(self, test, err) - _, _exc_str = self.errors[-1] - output = self.complete_output() - self.result.append((2, test, output, _exc_str)) - if self.verbosity > 1: - sys.stderr.write('E ') - sys.stderr.write(str(test)) - sys.stderr.write('\n') - else: - sys.stderr.write('E') - - def addFailure(self, test, err): - self.failure_count += 1 - TestResult.addFailure(self, test, err) - _, _exc_str = self.failures[-1] - output = self.complete_output() - self.result.append((1, test, output, _exc_str)) - if self.verbosity > 1: - sys.stderr.write('F ') - sys.stderr.write(str(test)) - sys.stderr.write('\n') - else: - sys.stderr.write('F') - - -class HTMLTestRunner(Template_mixin): - """ - """ - def __init__(self, stream=sys.stdout, verbosity=1, title=None, description=None): - self.stream = stream - self.verbosity = verbosity - 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 - - self.startTime = datetime.datetime.now() - - - def run(self, test): - "Run the given test case or test suite." - result = _TestResult(self.verbosity) - test(result) - self.stopTime = datetime.datetime.now() - self.generateReport(test, result) - 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 rmap.has_key(cls): - rmap[cls] = [] - classes.append(cls) - rmap[cls].append((n,t,o,e)) - r = [(cls, rmap[cls]) for cls in classes] - 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('Pass %s' % result.success_count) - if result.failure_count: status.append('Failure %s' % result.failure_count) - if result.error_count: status.append('Error %s' % result.error_count ) - if status: - status = ' '.join(status) - else: - status = 'none' - return [ - ('Start Time', startTime), - ('Duration', duration), - ('Status', status), - ] - - - def generateReport(self, test, 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, - ) - 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 = saxutils.escape(name), - value = saxutils.escape(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 = 0 - for n,t,o,e in cls_results: - if n == 0: np += 1 - elif n == 1: nf += 1 - else: ne += 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 - - 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' % (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) - - report = self.REPORT_TMPL % dict( - test_list = ''.join(rows), - count = str(result.success_count+result.failure_count+result.error_count), - Pass = str(result.success_count), - fail = str(result.failure_count), - error = str(result.error_count), - ) - 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) - tid = (n == 0 and 'p' or 'f') + 't%s.%s' % (cid+1,tid+1) - name = t.id().split('.')[-1] - doc = t.shortDescription() or "" - desc = doc and ('%s: %s' % (name, doc)) or name - 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): - # TODO: some problem with 'string_escape': it escape \n and mess up formating - # uo = unicode(o.encode('string_escape')) - uo = o.decode('latin-1') - else: - uo = o - if isinstance(e,str): - # TODO: some problem with 'string_escape': it escape \n and mess up formating - # ue = unicode(e.encode('string_escape')) - ue = e.decode('latin-1') - else: - ue = e - - script = self.REPORT_TEST_OUTPUT_TMPL % dict( - id = tid, - output = saxutils.escape(uo+ue), - ) - - row = tmpl % dict( - tid = tid, - Class = (n == 0 and 'hiddenRow' or 'none'), - style = n == 2 and 'errorCase' or (n == 1 and 'failCase' or 'none'), - desc = desc, - script = script, - status = self.STATUS[n], - ) - 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) diff --git a/HTMLTestRunner_cn.py b/HTMLTestRunner_cn.py new file mode 100644 index 0000000..3f1fe9d --- /dev/null +++ b/HTMLTestRunner_cn.py @@ -0,0 +1,1241 @@ +#-*- 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) diff --git a/README b/README deleted file mode 100644 index b1beea9..0000000 --- a/README +++ /dev/null @@ -1,5 +0,0 @@ -HTMLTestRunner is an extension to the Python standard library's unittest module. -It generates easy to use HTML test reports. HTMLTestRunner is released under a -BSD style license. - -Only a single file module HTMLTestRunner.py is needed to generate your report. diff --git a/README.md b/README.md new file mode 100644 index 0000000..13bd088 --- /dev/null +++ b/README.md @@ -0,0 +1,61 @@ +# 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的用法` diff --git a/changelog.md b/changelog.md new file mode 100644 index 0000000..5cb7a75 --- /dev/null +++ b/changelog.md @@ -0,0 +1,25 @@ +# changelog + ++ 20170925 + - 测试报告完全汉化,包括错误日志的中文处理 + - 针对selenium UI测试增加失败自动截图功能,截图自动轮播 + - 增加失败自动重试功能 + - 增加饼图统计 + - 同时兼容python2.x 和3.x ++ 20180402 + - 表格样式优化 + - 修复部分bug + - 增加截图组,可展示多张截图,首次打开自动播放 + - 增加仅展示最后一次运行结果,多次重试时,每个测试用例仅展示一次 ++ 20181213 + - 增加分类标签、通过率等,优化样式 + - 修复部分框架在SetUP中失败导致测试中断的问题导致 ErrorHandle的问题 + - 修复部分编码Bug + - 优化运行逻辑 + - 对js代码优化,修复部分多次运行run导致结果异常的bug + ++ 20200427 + - 修复页面小错误 (fzk27) + ++ 20200508 + - 开放跳过测试的统计,完善饼图统计 \ No newline at end of file diff --git a/img/1.png b/img/1.png new file mode 100644 index 0000000..1cc5a75 Binary files /dev/null and b/img/1.png differ diff --git a/img/2.png b/img/2.png new file mode 100644 index 0000000..350ac7a Binary files /dev/null and b/img/2.png differ diff --git a/img/3.png b/img/3.png new file mode 100644 index 0000000..4690a66 Binary files /dev/null and b/img/3.png differ diff --git a/img/4.gif b/img/4.gif new file mode 100644 index 0000000..84bbe75 Binary files /dev/null and b/img/4.gif differ diff --git a/img/5.gif b/img/5.gif new file mode 100644 index 0000000..0d4beae Binary files /dev/null and b/img/5.gif differ diff --git a/img/5.png b/img/5.png new file mode 100644 index 0000000..972d9c4 Binary files /dev/null and b/img/5.png differ diff --git a/img/6.png b/img/6.png new file mode 100644 index 0000000..d91bd8a Binary files /dev/null and b/img/6.png differ diff --git a/img/7.png b/img/7.png new file mode 100644 index 0000000..2e44f5f Binary files /dev/null and b/img/7.png differ diff --git a/img/8.png b/img/8.png new file mode 100644 index 0000000..5d7d7f7 Binary files /dev/null and b/img/8.png differ diff --git a/sample_test_report.html b/sample_test_report.html index 94947d8..84069dc 100644 --- a/sample_test_report.html +++ b/sample_test_report.html @@ -1,6066 +1,738 @@ - - - - - Unit Test Report - - - - - - - - - -
-

Unit Test Report

-

Start Time: 2011-03-28 09:00:12

-

Duration: 0:00:06.825000

-

Status: Pass 318 Failure 10 Error 6

- -

-
- - - -

Show -Summary -Failed -All -

- -------- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
Test Group/Test caseCountPassFailErrorView
minds.cgibin.test.test_cgi.TestCGI6600Detail
test_control
- - - - pass - - - - -
test_help
- - - - pass - - - - -
test_help_gettingstarted
- - - - pass - - - - -
test_help_proxyinstruction
- - - - pass - - - - -
test_root
- - - - pass - - - - -
test_updateParent_input_escape
- - - - pass - - - - -
minds.cgibin.test.test_history.TestHistory4040Detail
test_GET
- - - - fail - - - - -
test_indexnow_needed
- - - - fail - - - - -
test_query_needed
- - - - fail - - - - -
test_query_no_result_needed
- - - - fail - - - - -
minds.cgibin.test.test_weblib.TestWeblibCGI141310Detail
test_buildCategoryList
- - - - pass - - - - -
test_weblib
- - - - pass - - - - -
test_weblib_go
- - - - pass - - - - -
test_weblib_go_invalid
- - - - pass - - - - -
test_weblib_input_escape
- - - - pass - - - - -
test_weblib_input_escape_tag
- - - - pass - - - - -
test_weblib_query
- - - - pass - - - - -
test_weblib_query_sort_date
- - - - pass - - - - -
test_weblib_query_sort_tag
- - - - fail - - - - -
test_weblib_query_sort_title
- - - - pass - - - - -
test_weblib_tag
- - - - pass - - - - -
test_weblib_tag_sort_date
- - - - pass - - - - -
test_weblib_tag_sort_tag
- - - - pass - - - - -
test_weblib_tag_sort_title
- - - - pass - - - - -
minds.cgibin.test.test_weblibForm.TestWeblibForm101000Detail
test_GET_404
- - - - pass - - - - -
test_GET_URL_match
- - - - pass - - - - -
test_GET_new
- - - - pass - - - - -
test_GET_rid
- - - - pass - - - - -
test_PUT_404
- - - - pass - - - - -
test_PUT_char_workout
- - - - pass - - - - -
test_PUT_existing
- - - - pass - - - - -
test_PUT_illegal
- - - - pass - - - - -
test_PUT_input_escape
- - - - pass - - - - -
test_PUT_new
- - - - pass - - - - -
minds.cgibin.test.test_weblibMultiForm.TestWeblibMultiForm6600Detail
test_GET
- - - - pass - - - - -
test_POST_add
- - - - pass - - - - -
test_POST_add_new_tag
- - - - pass - - - - -
test_POST_illegal_tag
- - - - pass - - - - -
test_POST_input_escape
- - - - pass - - - - -
test_POST_remove
- - - - pass - - - - -
minds.cgibin.test.test_weblibTagCategorize.TestWeblibTagCategorize5500Detail
test_GET
- - - - pass - - - - -
test_POST
- - - - pass - - - - -
test_POST0
- - - - pass - - - - -
test_POST_illegal
- - - - pass - - - - -
test_POST_input_escape
- - - - pass - - - - -
minds.cgibin.test.test_weblibTagForm.TestTagForm9900Detail
test_GET
- - - - pass - - - - -
test_GET_404
- - - - pass - - - - -
test_POST_404
- - - - pass - - - - -
test_POST_category_collapse
- - - - pass - - - - -
test_POST_invalid
- - - - pass - - - - -
test_POST_merge
- - - - pass - - - - -
test_POST_rename
- - - - pass - - - - -
test_POST_rename_capitalization
- - - - pass - - - - -
test_PUT_input_escape
- - - - pass - - - - -
minds.cgibin.util.test_request.TestRequest7700Detail
test_cookie
pass
test_method
pass
test_others
pass
test_param
pass
test_rid
pass
test_str
pass
test_tid
pass
minds.cgibin.util.test_response.TestResponse7700Detail
test_CGIRenderer
pass
test_WeblibLayoutRenderer
pass
test_buildBookmarklet
pass
test_cookie
pass
test_jsEscapeString
- - - - pass - - - - -
test_redirect
pass
test_split_style_block
pass
minds.test.test_app_httpserver.TestAppHTTPRequestHandler1100Detail
test_lookup_cgi
pass
minds.test.test_app_httpserver.TestCGIFileFilter6600Detail
test1
pass
test_buffer
pass
test_location
pass
test_nodirective
pass
test_states
pass
test_status
pass
minds.test.test_app_httpserver.TestMisc3300Detail
test_convertPath2Module1
pass
test_convertPath2Module2
pass
test_convertPath2Module3
pass
minds.test.test_cachefile.TestCacheFile2200Detail
test_discard
pass
test_write
pass
minds.test.test_config.TestConfig101000Detail
testSafeConfig
- - - - pass - - - - -
test_get
- - - - pass - - - - -
test_get_notexist
- - - - pass - - - - -
test_getboolean
- - - - pass - - - - -
test_getdefault
- - - - pass - - - - -
test_getint
- - - - pass - - - - -
test_getpath
- - - - pass - - - - -
test_save
- - - - pass - - - - -
test_set
- - - - pass - - - - -
test_str
- - - - pass - - - - -
minds.test.test_distillML.TestCharEncoding8800Detail
test_bad_encoding
pass
test_big5
pass
test_big5_txt
pass
test_euc_jp
pass
test_euc_kr
pass
test_iso_8851_1
pass
test_koi8_r
pass
test_utf8
pass
minds.test.test_distillML.TestDistill10730Detail
testAttrEncodingProblem: Bad HTML found in http://news.bbc.co.uk/
pass
testDistill
pass
testDistillTxt
pass
testMeta
pass
testMetaVariations
pass
testParseCrazyTitleProblem
pass
testParseEmptyTagProblem: Test problem in parsing
pass
testParserError
- - - - fail - - - - -
testTextAsHTML_needed
- - - - fail - - - - -
testWordSpaceCollapseIssue_needed
- - - - fail - - - - -
minds.test.test_distillML.TestFormatter1100Detail
test_notifyHtml
pass
minds.test.test_distillML.TestWeeding8800Detail
testCSS
pass
testDomainFiltered
pass
testDomainFilteredTxt
pass
testFrameset
pass
testJavascript
pass
testLowvisible
pass
testMagicFiltered
pass
testMagicFilteredTxt: Wrong media type text/plain
pass
minds.test.test_distillparse.TestParseDistillML6600Detail
testMAX_OUTPUT_TAG_LEN
pass
testParse0: test parsing a minimal file
pass
testParse00: test parsing a empty file (invalid without the header section)
pass
testParseMeta: test parsing header into meta dictionary
pass
testParseTagSpanBuffer
pass
testParseTags
pass
minds.test.test_docarchive.TestArchiveHandler3201Detail
test_add_document
- - - - error - - - - -
test_append_to_exiting_archive
pass
test_invalid_mode
pass
minds.test.test_docarchive.TestDocArchive3201Detail
test_get_document
- - - - error - - - - -
test_get_document_not_exist
pass
test_invalideId
pass
minds.test.test_docarchive.TestIdCounter5401Detail
test_findIdRange
pass
test_findIdRange_initial_state
pass
test_findIdRange_no_file_in_zip
- - - - error - - - - -
test_findIdRange_resist_garbagefile
pass
test_getNewId
- - - - pass - - - - -
minds.test.test_domain_filter.TestDomainFilter4400Detail
testFilter0
pass
testFilter1
pass
testLoad
pass
testLoad0
pass
minds.test.test_encode_tools.TestEncodeTools111100Detail
test_determine0
pass
test_determine_DEFAULT
pass
test_determine_HTTP_CONTENT_TYPE
pass
test_determine_META_CHARSET0
pass
test_determine_META_CHARSET1
pass
test_determine_lenient
pass
test_findCharSet0
pass
test_findCharSet1
pass
test_findCharSetX: eXtreme findCharSet
pass
test_getreader
pass
test_getreader_invalid
pass
minds.test.test_lucene_logic.TestLuceneLogic3003Detail
test_FSDirectory
- - - - error - - - - -
test_RAM
- - - - error - - - - -
test_version
- - - - error - - - - -
minds.test.test_messagelog.TestMessageInfo5410Detail
testDiscardFilter
- - - - fail - - - - -
testParseMessageLog
pass
testParseMessageLog00
pass
testParseMessageLog01
pass
testTruncated
- - - - pass - - - - -
minds.test.test_messagelog.TestMsgLogger6600Detail
testDispose_00
- - - - pass - - - - -
testDispose_01
- - - - pass - - - - -
testDispose_10
- - - - pass - - - - -
testDispose_11
- - - - pass - - - - -
testLastIssued
- - - - pass - - - - -
test_findHighestId
pass
minds.test.test_proxyhandler.TestProxyHandler5500Detail
testDiscarded
- - - - pass - - - - -
testException
- - - - pass - - - - -
testHandlerOverflow
- - - - pass - - - - -
testNextProxy
- - - - pass - - - - -
testRequestForwarded
- - - - pass - - - - -
minds.test.test_upgrade_checker.TestUpgrade5500Detail
test_checkUpgrade
- - - - pass - - - - -
test_checkUpgrade_failed_fetch
- - - - pass - - - - -
test_fetch
pass
test_pollUpgradeInfo
- - - - pass - - - - -
test_set_config
- - - - pass - - - - -
minds.util.httputil.TestHttputil2200Detail
test_canonicalize
pass
test_split
pass
minds.util.multiblockfile.TestReaderWriter3300Detail
testFillerline
pass
testReadWrite
- - - - pass - - - - -
testTellNSeek
- - - - pass - - - - -
minds.util.patterns_tester.TestCheckStrings7700Detail
test00
pass
test01
pass
test10
pass
testCheckedOK
pass
testNoPatternBad
pass
testNoPatternGood
pass
testOrderWrong
pass
minds.util.patterns_tester.TestPatternTester8800Detail
test00
pass
test01
pass
test10
pass
testCheckedOK
pass
testCheckedRe
pass
testNoPatternBad
pass
testNoPatternGood
pass
testOrderWrong
pass
minds.util.test_dateutil.TestDateUtil2200Detail
test_isoformat
pass
test_parse
pass
minds.util.test_fileutil.TestBoundedFile3300Detail
test_boundedFile
pass
test_boundedFile1
pass
test_boundedFileDelegation
pass
minds.util.test_fileutil.TestFileUtil2200Detail
test_RecordFile
- - - - pass - - - - -
test_shift_files
pass
minds.util.test_html_pull_parser.TestParser4400Detail
test_0
pass
test_comment
pass
test_no_retain
pass
test_parse
pass
minds.util.test_html_pull_parser.TestSGMLPatch5500Detail
test_declaration_bad
pass
test_declaration_good_case
pass
test_declaration_incomplete
pass
test_parse_emptytag
pass
test_xml_CDATA
pass
minds.util.test_magic.TestMagic131300Detail
test_empty_file
pass
test_gif
pass
test_ico
pass
test_jpeg
pass
test_partial_mask_match0
pass
test_partial_mask_match1
pass
test_partial_match0
pass
test_partial_match1
pass
test_png
pass
test_text_html
pass
test_uft16
pass
test_utf8
pass
test_zip
pass
minds.util.test_pagemeter.TestPageMeter4400Detail
test0
pass
test100
pass
test105
pass
testError
pass
minds.util.test_rspreader.TestChunked101000Detail
test0
pass
testBadLength
pass
testEmpty
pass
testErrorOnSecondBlock
pass
testIncompleteChunk
pass
testInvalidEOC
pass
testNegativeLength
pass
testNoLength
pass
testOneBlock
pass
testTwoBlocks
pass
minds.util.test_rspreader.TestContentReader8800Detail
test0
pass
testChunked
pass
testChunkedGzip
pass
test_deflate
pass
test_empty_response
pass
test_gzip_encoding
pass
test_no_encoding
pass
test_no_encoding_controlled: controlled test of test_no_encoding() without using ContentReader
pass
minds.util.test_rspreader.TestOpen4400Detail
testOpenMlog
pass
testOpenMlogBinary
pass
testOpenMlog_controlled
pass
testOpenRegularDoc
pass
minds.util.test_rspreader.TestRspReader2200Detail
test_RspReader
pass
test_RspReader_controlled: controlled test of test_RspReader() without using RspReader
pass
minds.util.test_threadutil.Test_PooledExecutor2200Detail
test_PooledExecutor: test PooledExecutor
- - - - pass - - - - -
test_exception: Test task throws exception
- - - - pass - - - - -
minds.weblib.test.test_graph.TestIndentedTextParsing7700Detail
test0
pass
test_bfs
pass
test_deformed
pass
test_delete
pass
test_dfs
pass
test_rename
pass
test_simple
pass
minds.weblib.test.test_graph.TestUtils1100Detail
test_find_branches
pass
minds.weblib.test.test_import.TestImport121200Detail
test_ctime_str_2_iso8601
pass
test_delicious
- - - - pass - - - - -
test_delicious_bad
- - - - pass - - - - -
test_import_bookmarks
- - - - pass - - - - -
test_import_netscape_PushBackIterator
pass
test_import_tree
- - - - pass - - - - -
test_netscape
- - - - pass - - - - -
test_netscape_bad
- - - - pass - - - - -
test_netscape_via_IE
- - - - pass - - - - -
test_netscape_via_safari
- - - - pass - - - - -
test_opera
- - - - pass - - - - -
test_opera_bad
- - - - pass - - - - -
minds.weblib.test.test_query.TestQuery5500Detail
test_find_url
pass
test_query
pass
test_queryRoot
pass
test_query_by_tags
pass
test_query_tags
pass
minds.weblib.test.test_store.TestDsvUtil3300Detail
test_encode_and_decode
pass
test_row_object
pass
test_row_object_compatibility
pass
minds.weblib.test.test_store.TestStore202000Detail
test_change_n_save
pass
test_column_compatibility
- - - - pass - - - - -
test_dsv_encode_error
- - - - pass - - - - -
test_getWriter
- - - - pass - - - - -
test_init
pass
test_load
pass
test_load0
pass
test_load_n_save
pass
test_refresh_when_needed
- - - - pass - - - - -
test_remove_tag
pass
test_remove_webpage
pass
test_timestamp
pass
test_upgrade
- - - - pass - - - - -
test_write_name_value
- - - - pass - - - - -
test_write_tag_duplicated
pass
test_write_tag_existing
pass
test_write_tag_new
pass
test_write_webpage_existing
pass
test_write_webpage_new
pass
test_write_webpage_values
pass
minds.weblib.test.test_util.TestIdList7700Detail
test0
pass
test3
pass
test_duplicates
pass
test_failfast
pass
test_first
pass
test_remove
pass
test_with_id
pass
minds.weblib.test.test_util.TestIdNameList111100Detail
test0
pass
test3
pass
test_blank
pass
test_duplicates
pass
test_failfast
pass
test_first
pass
test_invalid_rename
pass
test_remove
pass
test_rename
pass
test_rename_capitalization
pass
test_with_id
pass
minds.weblib.test.test_util.TestURLUtil1010Detail
test_url_util_need_test
- - - - fail - - - - -
minds.weblib.test.test_weblib.TestTag3300Detail
test0
pass
test_cleanIllegalChar
pass
test_hasIllegalChar
pass
minds.weblib.test.test_weblib.TestWebPage1100Detail
test_object
pass
minds.weblib.test.test_weblib.TestWeblib111100Detail
test_category_setdescription
- - - - pass - - - - -
test_default_tag
pass
test_editTags
pass
test_makeTags
- - - - pass - - - - -
test_makeTags_duplicated
- - - - pass - - - - -
test_makeTags_invalid
pass
test_makeTags_order
- - - - pass - - - - -
test_setCategoryCollapse
pass
test_tag_merge_del
- - - - pass - - - - -
test_tag_rename
- - - - pass - - - - -
test_visit
pass
Total334318106 
- -
 
- - - + + + + + 带截图的测试报告 + + + + + + + + +
+

带截图的测试报告

+

开始时间: 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_014121详情 
test_case1: 百度搜索 + 呵呵呵呵 +
+ + + + + 通过 + + + + + 无截图
test_case2: 搜狗首页_retry:2
+ + + + + 失败 + + + + + + 显示截图 + +
test_case3: QQ邮箱_retry:2
+ + + + + 失败 + + + + + + 显示截图 + +
test_case4: 淘宝_retry:2
+ + + + + 错误 + + + + + + 显示截图 + +
case_023300详情 
test_case1: 百度搜索 + 呵呵呵呵 +
+ + + + + 通过 + + + + + 无截图
test_case2: 搜狗首页
+ + + + + 跳过 + + + + + 无截图
test_case3: QQ邮箱
+ + + + + 通过 + + + + + 无截图
test_case4: 淘宝
通过无截图
统计8421  
+ + +
 
+ + + diff --git a/sample_test_report_appium.html b/sample_test_report_appium.html new file mode 100644 index 0000000..766a4c5 --- /dev/null +++ b/sample_test_report_appium.html @@ -0,0 +1,498 @@ + + + + + 带截图的测试报告 + + + + + + + + +
+

带截图的测试报告

+

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

+

耗时: 0:00:36.348000

+

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

+ +

小试牛刀

+
+ + +
+
+ +
+
+ +
+概要[100.00%] +通过[1] +失败[0] +错误[0] + +所有[1] +
+ + +++++++++ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
测试组/测试用例总数通过失败错误视图错误截图
case_011100详情 
test_case1: 手机QQ截图
通过 + 显示截图 + +
统计1100  
+ + +
 
+ + + diff --git a/test_HTMLTestRunner.py b/test_HTMLTestRunner.py deleted file mode 100644 index 3967ce8..0000000 --- a/test_HTMLTestRunner.py +++ /dev/null @@ -1,253 +0,0 @@ -# -*- coding: utf-8 -*- - -import StringIO -import sys -import unittest - -import HTMLTestRunner - -# ---------------------------------------------------------------------- - -def safe_unicode(obj, *args): - """ return the unicode representation of obj """ - try: - return unicode(obj, *args) - except UnicodeDecodeError: - # obj is byte string - ascii_text = str(obj).encode('string_escape') - return unicode(ascii_text) - -def safe_str(obj): - """ return the byte string representation of obj """ - try: - return str(obj) - except UnicodeEncodeError: - # obj is unicode - return unicode(obj).encode('unicode_escape') - -# ---------------------------------------------------------------------- -# Sample tests to drive the HTMLTestRunner - -class SampleTest0(unittest.TestCase): - """ A class that passes. - - This simple class has only one test case that passes. - """ - def __init__(self, methodName): - unittest.TestCase.__init__(self, methodName) - - def test_pass_no_output(self): - """ test description - """ - pass - -class SampleTest1(unittest.TestCase): - """ A class that fails. - - This simple class has only one test case that fails. - """ - def test_fail(self): - u""" test description (描述) """ - self.fail() - -class SampleOutputTestBase(unittest.TestCase): - """ Base TestCase. Generates 4 test cases x different content type. """ - def test_1(self): - print self.MESSAGE - def test_2(self): - print >>sys.stderr, self.MESSAGE - def test_3(self): - self.fail(self.MESSAGE) - def test_4(self): - raise RuntimeError(self.MESSAGE) - -class SampleTestBasic(SampleOutputTestBase): - MESSAGE = 'basic test' - -class SampleTestHTML(SampleOutputTestBase): - MESSAGE = 'the message is 5 symbols: <>&"\'\nplus the HTML entity string: [©] on a second line' - -class SampleTestLatin1(SampleOutputTestBase): - MESSAGE = u'the message is áéíóú'.encode('latin-1') - -class SampleTestUnicode(SampleOutputTestBase): - u""" Unicode (統一碼) test """ - MESSAGE = u'the message is \u8563' - # 2006-04-25 Note: Exception would show up as - # AssertionError: - # - # This seems to be limitation of traceback.format_exception() - # Same result in standard unittest. - - # 2011-03-28 Note: I think it is fixed in Python 2.6 - def test_pass(self): - u""" A test with Unicode (統一碼) docstring """ - pass - - -# ------------------------------------------------------------------------ -# This is the main test on HTMLTestRunner - -class Test_HTMLTestRunner(unittest.TestCase): - - def test0(self): - self.suite = unittest.TestSuite() - buf = StringIO.StringIO() - runner = HTMLTestRunner.HTMLTestRunner(buf) - runner.run(self.suite) - # didn't blow up? ok. - self.assert_('' in buf.getvalue()) - - def test_main(self): - # Run HTMLTestRunner. Verify the HTML report. - - # suite of TestCases - self.suite = unittest.TestSuite() - self.suite.addTests([ - unittest.defaultTestLoader.loadTestsFromTestCase(SampleTest0), - unittest.defaultTestLoader.loadTestsFromTestCase(SampleTest1), - unittest.defaultTestLoader.loadTestsFromTestCase(SampleTestBasic), - unittest.defaultTestLoader.loadTestsFromTestCase(SampleTestHTML), - unittest.defaultTestLoader.loadTestsFromTestCase(SampleTestLatin1), - unittest.defaultTestLoader.loadTestsFromTestCase(SampleTestUnicode), - ]) - - # Invoke TestRunner - buf = StringIO.StringIO() - #runner = unittest.TextTestRunner(buf) #DEBUG: this is the unittest baseline - runner = HTMLTestRunner.HTMLTestRunner( - stream=buf, - title='', - description='This demonstrates the report output by HTMLTestRunner.' - ) - runner.run(self.suite) - - # Define the expected output sequence. This is imperfect but should - # give a good sense of the well being of the test. - EXPECTED = u""" -Demo Test - ->SampleTest0: - ->SampleTest1: - ->SampleTestBasic ->test_1< -pass -basic test - ->test_2< -pass -basic test - ->test_3< -fail -AssertionError: basic test - ->test_4< -error -RuntimeError: basic test - - ->SampleTestHTML ->test_1< -pass -the message is 5 symbols: <>&"' -plus the HTML entity string: [&copy;] on a second line - ->test_2< -pass -the message is 5 symbols: <>&"' -plus the HTML entity string: [&copy;] on a second line - ->test_3< -fail -AssertionError: the message is 5 symbols: <>&"' -plus the HTML entity string: [&copy;] on a second line - ->test_4< -error -RuntimeError: the message is 5 symbols: <>&"' -plus the HTML entity string: [&copy;] on a second line - - ->SampleTestLatin1 ->test_1< -pass -the message is áéíóú - ->test_2< -pass -the message is áéíóú - ->test_3< -fail -AssertionError: the message is áéíóú - ->test_4< -error -RuntimeError: the message is áéíóú - - ->SampleTestUnicode ->test_1< -pass -the message is \u8563 - ->test_2< -pass -the message is \u8563 - ->test_3< -fail -AssertionError: - ->test_4< -error -RuntimeError: - -Total ->19< ->10< ->5< ->4< - -""" - # check out the output - byte_output = buf.getvalue() - # output the main test output for debugging & demo - print byte_output - # HTMLTestRunner pumps UTF-8 output - output = byte_output.decode('utf-8') - self._checkoutput(output,EXPECTED) - - - def _checkoutput(self,output,EXPECTED): - i = 0 - for lineno, p in enumerate(EXPECTED.splitlines()): - if not p: - continue - j = output.find(p,i) - if j < 0: - self.fail(safe_str('Pattern not found lineno %s: "%s"' % (lineno+1,p))) - i = j + len(p) - - - - -############################################################################## -# Executing this module from the command line -############################################################################## - -import unittest -if __name__ == "__main__": - if len(sys.argv) > 1: - argv = sys.argv - else: - argv=['test_HTMLTestRunner.py', 'Test_HTMLTestRunner'] - unittest.main(argv=argv) - # Testing HTMLTestRunner with HTMLTestRunner would work. But instead - # we will use standard library's TextTestRunner to reduce the nesting - # that may confuse people. - #HTMLTestRunner.main(argv=argv) - diff --git a/test_screenshot_appium.py b/test_screenshot_appium.py new file mode 100644 index 0000000..8772de4 --- /dev/null +++ b/test_screenshot_appium.py @@ -0,0 +1,54 @@ +# -*- 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) diff --git a/test_screenshot_selenium.py b/test_screenshot_selenium.py new file mode 100644 index 0000000..73631c1 --- /dev/null +++ b/test_screenshot_selenium.py @@ -0,0 +1,150 @@ +# -*- 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)