diff --git a/Business/Class/HTMLTestRunner.py b/Business/Class/HTMLTestRunner.py new file mode 100644 index 0000000..1c4d327 --- /dev/null +++ b/Business/Class/HTMLTestRunner.py @@ -0,0 +1,1329 @@ +""" +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.STYLES_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. +""" + +import os, re, sys, io, time, datetime, json, unittest, logging +from xml.sax import saxutils + +_global_dict = {} + + +class GlobalMsg(object): + def __init__(self): + global _global_dict + _global_dict = {} + + @staticmethod + def set_value(name, value): + _global_dict[name] = value + + @staticmethod + def get_value(name): + try: + return _global_dict[name] + except KeyError: + return None + + +# ------------------------------------------------------------------------ +# The redirectors below are used to capture output during testing. Output +# sent to sys.stdout and sys.stderr are automatically captured. However +# in some cases sys.stdout is already cached before HTMLTestRunner is +# invoked (e.g. calling logging.basicConfig). In order to capture those +# output, use the redirectors for the cached stream. +# +# e.g. +# >>> logging.basicConfig(stream=HTMLTestRunner.stdout_redirector) +# >>> + + +class OutputRedirector(object): + """ Wrapper to redirect stdout or stderr """ + + def __init__(self, fp): + self.fp = fp + + def write(self, s): + self.fp.write(s) + + def writelines(self, lines): + self.fp.writelines(lines) + + def flush(self): + self.fp.flush() + + +stdout_redirector = OutputRedirector(sys.stdout) +stderr_redirector = OutputRedirector(sys.stderr) + + +# ---------------------------------------------------------------------- +# Template + + +class Template_mixin(object): + """ + Define a HTML template for report customerization and generation. + + Overall structure of an HTML report + + HTML + +------------------------+ + | | + | | + | | + | STYLES | + | +----------------+ | + | | | | + | +----------------+ | + | | + | | + | | + | | + | | + | HEADER | + | +----------------+ | + | | | | + | +----------------+ | + | | + | REPORT | + | +----------------+ | + | | | | + | +----------------+ | + | | + | FOOTER | + | +----------------+ | + | | | | + | +----------------+ | + | | + | | + | | + +------------------------+ + """ + + STATUS = { + 0: '通过', + 1: '失败', + 2: '错误', + } + + DEFAULT_TITLE = '测试报告' + DEFAULT_DESCRIPTION = '' + DEFAULT_TESTER = 'Tester' + + # ------------------------------------------------------------------------ + # 网页模板开始 + # 网页模板,变量列表 title, generator, styles, header, report, footer + HTML_TMPL = r""" + + + %(title)s + + + + + + + + + %(styles)s + + + +%(header)s +%(report)s +%(footer)s +
+ + %(sign)s +
+ + +""" + # 网页模板结束 + # ------------------------------------------------------------------------ + + # ------------------------------------------------------------------------ + # Stylesheet + # + # alternatively use a for external style sheet, e.g. + # + + STYLES_TMPL = """ + + """ + + # ------------------------------------------------------------------------ + # 头部信息开始 + # 添加显示截图和统计图div,变量列表 title, parameters, description + HEADER_TMPL = """ +
+
+
+ +
+
+
+
+

%(title)s

+ %(parameters)s +

%(description)s

+
+
+
+
+ """ + + # 测试信息模板,变量列表 name, value + HEADER_ATTRIBUTE_TMPL = """ +

%(name)s : %(value)s

+ """ + # 头部信息结束 + # ------------------------------------------------------------------------ + + # ------------------------------------------------------------------------ + # 报告模板开始 + # 变量列表 test_list, counts, passed, failed, errors ,passrate + REPORT_TMPL = """ +
+

+ 概要 %(passrate)s + 通过 %(passed)s + 失败 %(failed)s + 错误 %(errors)s + 全部 %(counts)s +

+
+ + + + + + + + + + + + + + + + + + + + + + %(test_list)s + + + + + + + + + +
测试用例说明总计通过失败错误耗时详细
总计%(counts)s%(passed)s%(failed)s%(errors)s%(time_usage)s通过:%(passrate)s
+ """ + + # 变量列表 style, desc, counts, passed, failed, errors, cid + REPORT_CLASS_TMPL = """ + + %(name)s + %(docs)s + %(counts)s + %(passed)s + %(failed)s + %(errors)s + %(time_usage)s + 查看全部 + + """ + + # 失败样式(有截图列),变量列表 tid, Class, style, desc, status + REPORT_TEST_WITH_OUTPUT_TMPL_1 = """ + +
%(name)s
+ %(docs)s + + +
+
%(script)s
+
+ + +
截图信息
+
+ %(screenshot)s +
+ + + """ + + # 失败样式(无截图列),变量列表 tid, Class, style, desc, status + REPORT_TEST_WITH_OUTPUT_TMPL_0 = """ + +
%(name)s
+ %(docs)s + + +
+
%(script)s
+
+ + + + """ + + # 通过样式,变量列表 tid, Class, style, desc, status + REPORT_TEST_NO_OUTPUT_TMPL = """ + +
%(name)s
+ %(docs)s + + + + """ + + # 测试输出内容 + REPORT_TEST_OUTPUT_TMPL = '%(id)s:' + "\n" + '%(output)s' + + # 返回顶部按钮 + FOOTER_TMPL = """ + + + """ + # 报告模板结束 + # ------------------------------------------------------------------------ + + +def _findMark(mark='', text=''): + return re.findall('\[' + mark + '](.*?)\[/' + mark + ']' + '*?', text) + + +def _makeMark(mark='', cont=''): + return '[' + mark + ']' + cont +'[/' + mark + ']' + + +def _Color(fc=0, bc=0, bo=0, text=''): + if "PYCHARM" not in os.environ.keys(): + return text + return "\033[" + str(bo) + ['', ';' + str(fc)][fc > 0] + ['', ';' + str(bc)][bc > 0] + "m" + text + "\033[0m" + + +class _TestResult(unittest.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, log=None): + super().__init__(verbosity=verbosity) + self.fh = None + self.lh = None + self.ch = None + self.verbosity = verbosity + self.logoutput = log + self.loggerStream = None + self.outputBuffer = None + self.stdout0 = None + self.stderr0 = None + self.passed_count = 0 + self.failed_count = 0 + self.errors_count = 0 + self.stime = None + self.etime = None + # 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 = [] + self.passrate = float(0) + # 分类统计数量耗时 + self.casesort = {} + # 增加失败用例合集 + self.failedCase = "" + self.failedCaseList = [] + # 增加错误用例合集 + self.errorsCase = "" + self.errorsCaseList = [] + + self.logger = logging.getLogger('test') + + def sortCount(self, cls, res, dur=float(0)): + s = self.casesort + if cls not in s.keys(): + s[cls] = {"p": 0, "f": 0, "e": 0, "d": 0} + if str(res).upper().startswith('P'): + s[cls]['p'] += 1 + if str(res).upper().startswith('F'): + s[cls]['f'] += 1 + if str(res).upper().startswith('E'): + s[cls]['e'] += 1 + s[cls]['d'] += dur + return True + + def startTest(self, test): + # 单条用例执行开始前的动作 + if self.verbosity >= 2: + sys.stderr.write(_Color(fc=39, bc=4, bo=1, text='%-79s' % ('%04d Testing...' % (len(self.result) + 1))) + "\n") + super().startTest(test) + if self.verbosity >= 0: + self.loggerStream = io.StringIO() + self.lh = logging.StreamHandler(self.loggerStream) + self.lh.setLevel(logging.DEBUG) + self.lh.setFormatter(logging.Formatter(fmt='{asctime} - {levelname[0]}: {message}', style='{')) + self.logger.addHandler(self.lh) + if self.verbosity >= 2: + self.ch = logging.StreamHandler(sys.stderr) + self.ch.setLevel(logging.DEBUG) + self.ch.setFormatter(logging.Formatter(fmt='{asctime} - {levelname[0]}: {message}', style='{')) + self.logger.addHandler(self.ch) + if self.logoutput: + self.fh = logging.FileHandler(filename=self.logoutput, mode='a+', encoding='utf-8') + self.fh.setLevel(logging.DEBUG) + self.fh.setFormatter(logging.Formatter(fmt='{asctime} - {levelname[0]}: {module}.{funcName}\t{message}', style='{')) + self.logger.addHandler(self.fh) + # Just one buffer for both stdout and stderr + self.outputBuffer = io.StringIO() + 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 + self.stime = round(time.time(), 3) + + def complete_output(self): + # 单条用例执行结束后,添加结果前的动作 + # 添加结果需要调用的方法 + """ + Disconnect output redirection and return buffer. + Safe to call multiple times. + """ + self.etime = round(time.time(), 3) + if self.stdout0: + sys.stdout = self.stdout0 + sys.stderr = self.stderr0 + self.stdout0 = None + self.stderr0 = None + return [self.loggerStream.getvalue(), self.outputBuffer.getvalue()] + + def stopTest(self, test): + # 单条用例执行结束后,添加结果后的动作 + super().stopTest(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() + # 移除日志Handler + for value in [self.fh, self.lh, self.ch]: + if value: + self.logger.removeHandler(value) + + def addSuccess(self, test): + # 单条用例执行结束后,添加结果时的动作 + term_mark = '=' + term_head = 'Passed' + term_clor = 32 + self.passed_count += 1 + super().addSuccess(test) + output = self.complete_output() + utime = round(self.etime - self.stime, 3) + self.sortCount(cls=test.__class__.__qualname__, res=term_head, dur=utime) + self.result.append((0, test, output[1], '', utime)) + # 单条用例执行结束后在终端打印结果 + if self.verbosity >= 1: + sys.stderr.write('%s %s %s %s.%s.%-18s\t%s %s\n' % ( + _Color(fc=term_clor, bo=1, text='%04d' % (len(self.result)) + ' ' + term_mark + ' ' + term_head + ':'), + _Color(fc=37, bo=0, text=datetime.datetime.utcfromtimestamp(utime).strftime('%H:%M:%S.%f')[0:12]), + _Color(fc=term_clor, bo=1, text='<='), + _Color(fc=37, bo=0, text=str(test.__module__).strip('_')), + _Color(fc=37, bo=0, text=str(test.__class__.__qualname__)), + _Color(fc=37, bo=0, text=str(test.__dict__["_testMethodName"])), + _Color(fc=term_clor, bo=1, text='<='), + _Color(fc=37, bo=0, text=str(test.__dict__["_testMethodDoc"] or "")) + )) + + def addError(self, test, err): + # 单条用例执行结束后,添加结果时的动作 + term_mark = '?' + term_head = 'Errors' + term_clor = 33 + self.errors_count += 1 + super().addError(test, err) + _, _exc_str = self.errors[-1] + output = self.complete_output() + utime = round(self.etime - self.stime, 3) + self.sortCount(cls=test.__class__.__qualname__, res=term_head, dur=utime) + self.result.append((2, test, output[0] + output[1], _exc_str, utime)) + # 单条用例执行结束后在终端打印结果 + if self.verbosity >= 1: + sys.stderr.write('%s %s %s %s.%s.%-18s\t%s %s\n' % ( + _Color(fc=term_clor, bo=1, text='%04d' % (len(self.result)) + ' ' + term_mark + ' ' + term_head + ':'), + _Color(fc=37, bo=0, text=datetime.datetime.utcfromtimestamp(utime).strftime('%H:%M:%S.%f')[0:12]), + _Color(fc=term_clor, bo=1, text='<='), + _Color(fc=37, bo=0, text=str(test.__module__).strip('_')), + _Color(fc=37, bo=0, text=str(test.__class__.__qualname__)), + _Color(fc=37, bo=0, text=str(test.__dict__["_testMethodName"])), + _Color(fc=term_clor, bo=1, text='<='), + _Color(fc=37, bo=0, text=str(test.__dict__["_testMethodDoc"] or "")) + )) + # 收集错误测试用例名称以在测试报告中显示 + casename = str(test.__module__).strip('_') + '.' + str(test.__class__.__qualname__) + '.' + str( + test.__dict__["_testMethodName"]) + self.errorsCase += "
  • " + casename + "
  • " + self.errorsCaseList.append(casename) + + def addFailure(self, test, err): + # 单条用例执行结束后,添加结果时的动作 + term_mark = '!' + term_head = 'Failed' + term_clor = 31 + self.failed_count += 1 + super().addFailure(test, err) + _, _exc_str = self.failures[-1] + output = self.complete_output() + utime = round(self.etime - self.stime, 3) + self.sortCount(cls=test.__class__.__qualname__, res=term_head, dur=utime) + self.result.append((1, test, output[0] + output[1], _exc_str, utime)) + # 单条用例执行结束后在终端打印结果 + if self.verbosity >= 1: + sys.stderr.write('%s %s %s %s.%s.%-18s\t%s %s\n' % ( + _Color(fc=term_clor, bo=1, text='%04d' % (len(self.result)) + ' ' + term_mark + ' ' + term_head + ':'), + _Color(fc=37, bo=0, text=datetime.datetime.utcfromtimestamp(utime).strftime('%H:%M:%S.%f')[0:12]), + _Color(fc=term_clor, bo=1, text='<='), + _Color(fc=37, bo=0, text=str(test.__module__).strip('_')), + _Color(fc=37, bo=0, text=str(test.__class__.__qualname__)), + _Color(fc=37, bo=0, text=str(test.__dict__["_testMethodName"])), + _Color(fc=term_clor, bo=1, text='<='), + _Color(fc=37, bo=0, text=str(test.__dict__["_testMethodDoc"] or "")) + )) + # 收集失败测试用例名称以在测试报告中显示 + casename = str(test.__module__).strip('_') + '.' + str(test.__class__.__qualname__) + '.' + str( + test.__dict__["_testMethodName"]) + self.failedCase += "
  • " + casename + "
  • " + self.failedCaseList.append(casename) + + +class HTMLTestRunner(Template_mixin): + # 新增 errormsg 参数,-1为无需截图,否则需要截图 + def __init__(self, report=None, log=None, stream=None, verbosity=1, title=None, description=None, + info=None, logo='', sign=''): + self.passrate = None + self.errormsg = None + self.logspath = log and os.path.abspath(log) + self.logo = logo + self.sign = sign + self.stream = stream or (report and open(report, 'wb')) + self.verbosity = verbosity + self.runstime = None + self.runetime = None + + 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 + if info is None: + self.testinfo = [] + elif isinstance(info, list): + self.testinfo = info + elif isinstance(info, dict): + self.testinfo = [] + for key, value in info.items(): + self.testinfo.append([key, value]) + else: + self.testinfo = [] + + def run(self, test): + """ + Run the given test case or test suite. + """ + # The final result is output when the verbosity is 1, + # and the result of each use case is output when the verbosity is 2. + # Start testing. + sys.stderr.write(_Color( + fc=38, bo=1, text='* * * * * * * * * * * * * * * * * * 开始测试 * * * * * * * * * * * * * * * * * *') + '\n') + self.runstime = round(time.time(), 3) + result = _TestResult(verbosity=self.verbosity, log=self.logspath) + test(result) + self.runetime = round(time.time(), 3) + sys.stderr.write(_Color( + fc=38, bo=1, text='* * * * * * * * * * * * * * * * * * 结束测试 * * * * * * * * * * * * * * * * * *') + '\n') + # Generate test report. + self.generateReport(test, result) + case_count = { + "t": len(result.result), + "p": result.passed_count, + "f": result.failed_count, + "e": result.errors_count + } + dura = time.strftime('%H:%M:%S', time.gmtime((self.runetime - self.runstime))) + rate_passed = str("%.2f%%" % (float(case_count["p"] / case_count["t"] * 100))) + rate_failed = str("%.2f%%" % (float(case_count["f"] / case_count["t"] * 100))) + rate_errors = str("%.2f%%" % (float(case_count["e"] / case_count["t"] * 100))) + list_failed = result.failedCaseList + list_errors = result.errorsCaseList + casesort = result.casesort + sys.stderr.write(_Color(fc=36, bo=1, text='结果概要') + "\n") + sys.stderr.write(_Color(fc=37, bo=0, text='总共' + "\x20" + str(case_count["t"]) + "\x20") + + _Color(fc=37, bo=0, text='通过' + "\x20" + str(case_count["p"]) + "\x20") + + _Color(fc=37, bo=0, text='失败' + "\x20" + str(case_count["f"]) + "\x20") + + _Color(fc=37, bo=0, text='错误' + "\x20" + str(case_count["e"]) + "\x20") + + _Color(fc=37, bo=0, text='耗时' + "\x20" + dura + "\x20") + + _Color(fc=37, bo=0, text='通过率' + "\x20" + rate_passed + "\x20") + + _Color(fc=37, bo=0, text='失败率' + "\x20" + rate_failed + "\x20") + + _Color(fc=37, bo=0, text='错误率' + "\x20" + rate_errors + "\x20") + + "\n" + ) + for value in [['失败用例', list_failed, 31], ['错误用例', list_errors, 33]]: + if len(value[1]): + sys.stderr.write(_Color(fc=value[2], bo=1, text=value[0]) + "\n") + for i in range(len(value[1])): + sys.stderr.write(_Color(fc=37, bo=0, text=str(i + 1) + '. ' + value[1][i]) + "\n") + if len(casesort.keys()): + sys.stderr.write(_Color(fc=34, bo=1, + text='%-16s' % ('用例集合') + "\t" + + '%-4s' % ('总计') + "\t" + + '%-4s' % ('通过') + "\t" + + '%-4s' % ('失败') + "\t" + + '%-4s' % ('错误') + "\t" + + '%-8s' % ('耗时') + "\t") + "\n") + for key, value in casesort.items(): + sys.stderr.write(_Color(fc=37, bo=0, + text='%-16s' % (str(key)) + "\t" + + '%-4s' % (str(value["p"] + value["f"] + value["e"])) + "\t" + + '%-4s' % (str(value["p"])) + "\t" + + '%-4s' % (str(value["f"])) + "\t" + + '%-4s' % (str(value["e"])) + "\t" + + '%.3f' % (round(value["d"], 3)) + '秒' + "\t" + ) + "\n") + 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, s in result_list: + cls = t.__class__ + if cls not in rmap: + rmap[cls] = [] + classes.append(cls) + rmap[cls].append((n, t, o, e, s)) + return [(cls, rmap[cls]) for cls in classes] + + def getReportAttributes(self, result): + """ + Return report attributes as a list of (name, value). + Override this to add custom attributes. + """ + runstime = time.strftime('%Y-%m-%d %H:%M:%S', time.localtime(self.runstime)) + duration = time.strftime('%H:%M:%S', time.gmtime((self.runetime - self.runstime))) + status = ','.join([ + '总共 %s' % (result.passed_count + result.failed_count + result.errors_count), + '通过 %s' % (result.passed_count), + '失败 %s' % (result.failed_count), + '错误 %s' % (result.errors_count) + ]) + if (result.passed_count + result.failed_count + result.errors_count) > 0: + self.passrate = str("%.2f%%" % (float(result.passed_count) / float( + result.passed_count + result.failed_count + result.errors_count) * 100)) + else: + self.passrate = '0.00%' + + if len(result.failedCase) > 0: + failedCase = result.failedCase + else: + failedCase = '无' + + if len(result.errorsCase) > 0: + errorsCase = result.errorsCase + else: + errorsCase = '无' + + return self.testinfo + [ + ['开始时间', runstime], + ['合计耗时', duration], + ['测试结果', status + ',通过率 ' + self.passrate], + ['失败用例', failedCase], + ['错误用例', errorsCase], + ] + + def generateReport(self, test, result): + report_attrs = self.getReportAttributes(result) + report_count = self._generate_report(result) + output = self.HTML_TMPL % dict( + title=saxutils.escape(self.title), + logo=self.logo, + sign=self.sign, + generator='HTMLTestRunner', + styles=self._generate_styles(), + passed=report_count["passed"], + failed=report_count["failed"], + errors=report_count["errors"], + casesets=report_count["casesets"], + casesets_passed=report_count["casesets_passed"], + casesets_failed=report_count["casesets_failed"], + casesets_errors=report_count["casesets_errors"], + header=self._generate_header(report_attrs), + report=report_count["report"], + footer=self._generate_footer(), + ) + self.stream and self.stream.write(output.encode('utf-8')) + + def _generate_styles(self): + return self.STYLES_TMPL + + def _generate_header(self, report_attrs): + line_list = [] + for name, value in report_attrs: + match name: + case '失败用例': + if value == "无": + line = self.HEADER_ATTRIBUTE_TMPL % dict(name=name, value=value) + else: + line = self.HEADER_ATTRIBUTE_TMPL % dict(name=name, value="点击查看" + "
      " + value + "
    " + ) + case '错误用例': + if value == "无": + line = self.HEADER_ATTRIBUTE_TMPL % dict(name=name, value=value) + else: + line = self.HEADER_ATTRIBUTE_TMPL % dict(name=name, value="点击查看" + "
      " + value + "
    " + ) + case _: + line = self.HEADER_ATTRIBUTE_TMPL % dict(name=saxutils.escape(str(name)), value=saxutils.escape(str(value))) + line_list.append(line) + return self.HEADER_TMPL % dict( + title=saxutils.escape(self.title), + parameters=''.join(line_list), + description=saxutils.escape(self.description) + ) + + # 生成报告 + def _generate_report(self, result): + rows = [] + sortedResult = self.sortResult(result.result) + dura_caseset = 0 + for cid, (cls, cls_results) in enumerate(sortedResult): + np = nf = ne = ns = 0 + for n, t, o, e, s in cls_results: + # 遍历每条用例 + match n: + case 0: + np += 1 + case 1: + nf += 1 + case 2: + ne += 1 + ns += s + # 单个用例集合耗时 + ns = round(ns, 3) + dura_caseset += ns + row = self.REPORT_CLASS_TMPL % dict( + style=ne > 0 and 'errorClass' or nf > 0 and 'failClass' or 'passClass', + name=cls.__name__, + docs=cls.__doc__ and cls.__doc__.split("\n")[0] or '', + counts=np + nf + ne, + passed=np, + failed=nf, + errors=ne, + cid='c%s' % (cid + 1), + time_usage='%.3f' % (ns) + "秒" + ) + rows.append(row) + for tid, (n, t, o, e, s) in enumerate(cls_results): + rows.append(self._generate_report_test(rows, cid, tid, n, t, o, e)) + # 全部用例集合耗时 + dura_caseset = round(dura_caseset, 3) + report = self.REPORT_TMPL % dict( + test_list=''.join(rows), + counts=str(result.passed_count + result.failed_count + result.errors_count), + passed=str(result.passed_count), + failed=str(result.failed_count), + errors=str(result.errors_count), + time_usage='%.3f' % (dura_caseset) + "秒", + passrate=self.passrate + ) + casesets = list(result.casesort.keys()) + casesets_passed = [] + for value in casesets: + casesets_passed.append(result.casesort[value]["p"]) + casesets_failed = [] + for value in casesets: + casesets_failed.append(result.casesort[value]["f"]) + casesets_errors = [] + for value in casesets: + casesets_errors.append(result.casesort[value]["e"]) + return { + "report": report, + "passed": str(result.passed_count), + "failed": str(result.failed_count), + "errors": str(result.errors_count), + "casesets": json.dumps(casesets, ensure_ascii=False), + "casesets_passed": json.dumps(casesets_passed, ensure_ascii=False), + "casesets_failed": json.dumps(casesets_failed, ensure_ascii=False), + "casesets_errors": json.dumps(casesets_errors, ensure_ascii=False) + } + + def _generate_report_test(self, rows, cid, tid, n, t, o, e): + # e.g. 'pt1_1', 'ft1_1', 'et1_1' + hasout = bool(o or e) + # ID修改点为下划线,支持Bootstrap折叠展开特效 + match n: + case 0: + tid_flag = 'p' + case 1: + tid_flag = 'f' + case 2: + tid_flag = 'e' + case _: + tid_flag = 'u' + tid = tid_flag + 't%s_%s' % (cid + 1, tid + 1) + name = t.id().split('.')[-1] + docs = t.shortDescription() or "" + # o and e should be byte string because they are collected from stdout and stderr. + if isinstance(o, str): + # some problem with 'string_escape': it escape \n and mess up formating + # uo = unicode(o.encode('string_escape')) + # uo = o.decode('latin-1') + uo = o + else: + uo = o + if isinstance(e, str): + # some problem with 'string_escape': it escape \n and mess up formating + # ue = unicode(e.encode('string_escape')) + # ue = e.decode('latin-1') + ue = e + else: + ue = e + script = self.REPORT_TEST_OUTPUT_TMPL % dict(id=tid, output=re.compile('\[[A-Za-z]+].*?\[/[A-Za-z]+][\r\n]').sub( + '', saxutils.escape(uo + ue))) + + # 截图名称通过抛出异常在标准错误中 + output = uo + ue + # 先判断是否需要截图 + self.errormsg = output.find("TestError") + + if self.errormsg == -1: + # 没有截图信息 + template = hasout and self.REPORT_TEST_WITH_OUTPUT_TMPL_0 or self.REPORT_TEST_NO_OUTPUT_TMPL + row = template % dict( + tid=tid, + Class=n == 0 and 'hiddenRow' or 'none', + style=n == 2 and 'errorsCase' or (n == 1 and 'failedCase' or 'passedCase'), + name=name, + docs=docs, + script=script, + status=self.STATUS[n], + ) + else: + # 包含截图信息 + template = hasout and self.REPORT_TEST_WITH_OUTPUT_TMPL_1 or self.REPORT_TEST_NO_OUTPUT_TMPL + screenshot_list = _findMark(mark='TestErrorImg', text=output) + screenshot = '' + for value in screenshot_list: + try: + bn = os.path.basename(value) + except: + bn = '点击查看' + screenshot += '' + bn + '
    ' + row = template % dict( + tid=tid, + Class=n == 0 and 'hiddenRow' or 'none', + style=n == 2 and 'errorsCase' or (n == 1 and 'failedCase' or 'passedCase'), + name=name, + docs=docs, + script=script, + status=self.STATUS[n], + screenshot=screenshot + ) + return row + + def _generate_footer(self): + return self.FOOTER_TMPL diff --git a/HTMLTestRunner.py b/HTMLTestRunner.py index 32425aa..78a480a 100644 --- a/HTMLTestRunner.py +++ b/HTMLTestRunner.py @@ -303,15 +303,15 @@ class Template_mixin(object): }, series: [{ name: '通过', - color: '#64BB64', + color: '#64bb64', data: %(casesets_passed)s }, { name: '失败', - color: '#F16D7E', + color: '#f16d7e', data: %(casesets_failed)s }, { name: '错误', - color: '#FDC68C', + color: '#fdc68c', data: %(casesets_errors)s }] }) @@ -342,7 +342,7 @@ class Template_mixin(object): pie: { allowPointSelect: true, cursor: 'pointer', - colors: ['#81ca9d', '#f16d7e', '#fdc68c'], + colors: ['#64bb64', '#f16d7e', '#fdc68c'], dataLabels: { enabled: true, format: '{point.name}: {point.percentage:.1f} %%', @@ -505,7 +505,10 @@ function html_escape(s) { %(header)s %(report)s %(footer)s -
    +
    + + %(sign)s +
    """ @@ -632,7 +635,7 @@ function html_escape(s) {
    -
    +

    %(title)s

    %(parameters)s

    %(description)s

    @@ -790,9 +793,13 @@ class _TestResult(unittest.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): + def __init__(self, verbosity=1, log=None): super().__init__(verbosity=verbosity) + self.fh = None + self.lh = None + self.ch = None self.verbosity = verbosity + self.logoutput = log self.loggerStream = None self.outputBuffer = None self.stdout0 = None @@ -837,9 +844,25 @@ class _TestResult(unittest.TestResult): def startTest(self, test): # 单条用例执行开始前的动作 - if self.verbosity > 1: - sys.stderr.write(_Color(fc=30, bc=47, bo=1, text='%04d' % (len(self.result) + 1)) + ' ') + if self.verbosity >= 2: + sys.stderr.write(_Color(fc=39, bc=4, bo=1, text='%-79s' % ('%04d Testing...' % (len(self.result) + 1))) + "\n") super().startTest(test) + if self.verbosity >= 0: + self.loggerStream = io.StringIO() + self.lh = logging.StreamHandler(self.loggerStream) + self.lh.setLevel(logging.DEBUG) + self.lh.setFormatter(logging.Formatter(fmt='{asctime} - {levelname[0]}: {message}', style='{')) + self.logger.addHandler(self.lh) + if self.verbosity >= 2: + self.ch = logging.StreamHandler(sys.stderr) + self.ch.setLevel(logging.DEBUG) + self.ch.setFormatter(logging.Formatter(fmt='{asctime} - {levelname[0]}: {message}', style='{')) + self.logger.addHandler(self.ch) + if self.logoutput: + self.fh = logging.FileHandler(filename=self.logoutput, mode='a+', encoding='utf-8') + self.fh.setLevel(logging.DEBUG) + self.fh.setFormatter(logging.Formatter(fmt='{asctime} - {levelname[0]}: {module}.{funcName}\t{message}', style='{')) + self.logger.addHandler(self.fh) # Just one buffer for both stdout and stderr self.outputBuffer = io.StringIO() stdout_redirector.fp = self.outputBuffer @@ -849,12 +872,6 @@ class _TestResult(unittest.TestResult): sys.stdout = stdout_redirector sys.stderr = stderr_redirector self.stime = round(time.time(), 3) - self.loggerStream = io.StringIO() - self.ch = logging.StreamHandler(self.loggerStream) - self.ch.setLevel(logging.DEBUG) - self.ch.setFormatter( - logging.Formatter('%(asctime)s - %(name)s -%(levelname)s -%(process)d -%(processName)s - %(message)s')) - self.logger.addHandler(self.ch) def complete_output(self): # 单条用例执行结束后,添加结果前的动作 @@ -869,7 +886,7 @@ class _TestResult(unittest.TestResult): sys.stderr = self.stderr0 self.stdout0 = None self.stderr0 = None - return self.loggerStream.getvalue() + self.outputBuffer.getvalue() + return [self.loggerStream.getvalue(), self.outputBuffer.getvalue()] def stopTest(self, test): # 单条用例执行结束后,添加结果后的动作 @@ -879,7 +896,9 @@ class _TestResult(unittest.TestResult): # We must disconnect stdout in stopTest(), which is guaranteed to be called. # self.complete_output() # 移除日志Handler - self.logger.removeHandler(self.ch) + for value in [self.fh, self.lh, self.ch]: + if value: + self.logger.removeHandler(value) def addSuccess(self, test): # 单条用例执行结束后,添加结果时的动作 @@ -891,18 +910,18 @@ class _TestResult(unittest.TestResult): output = self.complete_output() utime = round(self.etime - self.stime, 3) self.sortCount(cls=test.__class__.__qualname__, res=term_head, dur=utime) - self.result.append((0, test, output, '', utime)) + self.result.append((0, test, output[1], '', utime)) # 单条用例执行结束后在终端打印结果 - if self.verbosity > 1: + if self.verbosity >= 1: sys.stderr.write('%s %s %s %s.%s.%-18s\t%s %s\n' % ( - _Color(fc=term_clor, bo=1, text=term_mark + ' ' + term_head + ':'), - datetime.datetime.utcfromtimestamp(utime).strftime('%H:%M:%S.%f')[0:12], + _Color(fc=term_clor, bo=1, text='%04d' % (len(self.result)) + ' ' + term_mark + ' ' + term_head + ':'), + _Color(fc=37, bo=0, text=datetime.datetime.utcfromtimestamp(utime).strftime('%H:%M:%S.%f')[0:12]), _Color(fc=term_clor, bo=1, text='<='), - str(test.__module__).strip('_'), - str(test.__class__.__qualname__), - str(test.__dict__["_testMethodName"]), + _Color(fc=37, bo=0, text=str(test.__module__).strip('_')), + _Color(fc=37, bo=0, text=str(test.__class__.__qualname__)), + _Color(fc=37, bo=0, text=str(test.__dict__["_testMethodName"])), _Color(fc=term_clor, bo=1, text='<='), - str(test.__dict__["_testMethodDoc"] or "") + _Color(fc=37, bo=0, text=str(test.__dict__["_testMethodDoc"] or "")) )) def addError(self, test, err): @@ -916,18 +935,18 @@ class _TestResult(unittest.TestResult): output = self.complete_output() utime = round(self.etime - self.stime, 3) self.sortCount(cls=test.__class__.__qualname__, res=term_head, dur=utime) - self.result.append((2, test, output, _exc_str, utime)) + self.result.append((2, test, output[0] + output[1], _exc_str, utime)) # 单条用例执行结束后在终端打印结果 - if self.verbosity > 1: + if self.verbosity >= 1: sys.stderr.write('%s %s %s %s.%s.%-18s\t%s %s\n' % ( - _Color(fc=term_clor, bo=1, text=term_mark + ' ' + term_head + ':'), - datetime.datetime.utcfromtimestamp(utime).strftime('%H:%M:%S.%f')[0:12], + _Color(fc=term_clor, bo=1, text='%04d' % (len(self.result)) + ' ' + term_mark + ' ' + term_head + ':'), + _Color(fc=37, bo=0, text=datetime.datetime.utcfromtimestamp(utime).strftime('%H:%M:%S.%f')[0:12]), _Color(fc=term_clor, bo=1, text='<='), - str(test.__module__).strip('_'), - str(test.__class__.__qualname__), - str(test.__dict__["_testMethodName"]), + _Color(fc=37, bo=0, text=str(test.__module__).strip('_')), + _Color(fc=37, bo=0, text=str(test.__class__.__qualname__)), + _Color(fc=37, bo=0, text=str(test.__dict__["_testMethodName"])), _Color(fc=term_clor, bo=1, text='<='), - str(test.__dict__["_testMethodDoc"] or "") + _Color(fc=37, bo=0, text=str(test.__dict__["_testMethodDoc"] or "")) )) # 收集错误测试用例名称以在测试报告中显示 casename = str(test.__module__).strip('_') + '.' + str(test.__class__.__qualname__) + '.' + str( @@ -946,18 +965,18 @@ class _TestResult(unittest.TestResult): output = self.complete_output() utime = round(self.etime - self.stime, 3) self.sortCount(cls=test.__class__.__qualname__, res=term_head, dur=utime) - self.result.append((1, test, output, _exc_str, utime)) + self.result.append((1, test, output[0] + output[1], _exc_str, utime)) # 单条用例执行结束后在终端打印结果 - if self.verbosity > 1: + if self.verbosity >= 1: sys.stderr.write('%s %s %s %s.%s.%-18s\t%s %s\n' % ( - _Color(fc=term_clor, bo=1, text=term_mark + ' ' + term_head + ':'), - datetime.datetime.utcfromtimestamp(utime).strftime('%H:%M:%S.%f')[0:12], + _Color(fc=term_clor, bo=1, text='%04d' % (len(self.result)) + ' ' + term_mark + ' ' + term_head + ':'), + _Color(fc=37, bo=0, text=datetime.datetime.utcfromtimestamp(utime).strftime('%H:%M:%S.%f')[0:12]), _Color(fc=term_clor, bo=1, text='<='), - str(test.__module__).strip('_'), - str(test.__class__.__qualname__), - str(test.__dict__["_testMethodName"]), + _Color(fc=37, bo=0, text=str(test.__module__).strip('_')), + _Color(fc=37, bo=0, text=str(test.__class__.__qualname__)), + _Color(fc=37, bo=0, text=str(test.__dict__["_testMethodName"])), _Color(fc=term_clor, bo=1, text='<='), - str(test.__dict__["_testMethodDoc"] or "") + _Color(fc=37, bo=0, text=str(test.__dict__["_testMethodDoc"] or "")) )) # 收集失败测试用例名称以在测试报告中显示 casename = str(test.__module__).strip('_') + '.' + str(test.__class__.__qualname__) + '.' + str( @@ -968,10 +987,14 @@ class _TestResult(unittest.TestResult): class HTMLTestRunner(Template_mixin): # 新增 errormsg 参数,-1为无需截图,否则需要截图 - def __init__(self, stream=None, verbosity=2, title=None, description=None, tester=None): + def __init__(self, report=None, log=None, stream=None, verbosity=1, title=None, description=None, + info=None, logo='', sign=''): self.passrate = None self.errormsg = None - self.stream = stream + self.logspath = log and os.path.abspath(log) + self.logo = logo + self.sign = sign + self.stream = stream or (report and open(report, 'wb')) self.verbosity = verbosity self.runstime = None self.runetime = None @@ -984,10 +1007,16 @@ class HTMLTestRunner(Template_mixin): self.description = self.DEFAULT_DESCRIPTION else: self.description = description - if tester is None: - self.tester = self.DEFAULT_TESTER + if info is None: + self.testinfo = [] + elif isinstance(info, list): + self.testinfo = info + elif isinstance(info, dict): + self.testinfo = [] + for key, value in info.items(): + self.testinfo.append([key, value]) else: - self.tester = tester + self.testinfo = [] def run(self, test): """ @@ -996,14 +1025,14 @@ class HTMLTestRunner(Template_mixin): # The final result is output when the verbosity is 1, # and the result of each use case is output when the verbosity is 2. # Start testing. - sys.stderr.write('********************************* 开始测试 *********************************' + '\n') - sys.stderr.write("\n") + sys.stderr.write(_Color( + fc=38, bo=1, text='* * * * * * * * * * * * * * * * * * 开始测试 * * * * * * * * * * * * * * * * * *') + '\n') self.runstime = round(time.time(), 3) - result = _TestResult(self.verbosity) + result = _TestResult(verbosity=self.verbosity, log=self.logspath) test(result) self.runetime = round(time.time(), 3) - sys.stderr.write("\n") - sys.stderr.write('********************************* 结束测试 *********************************' + '\n') + sys.stderr.write(_Color( + fc=38, bo=1, text='* * * * * * * * * * * * * * * * * * 结束测试 * * * * * * * * * * * * * * * * * *') + '\n') # Generate test report. self.generateReport(test, result) case_count = { @@ -1019,15 +1048,15 @@ class HTMLTestRunner(Template_mixin): list_failed = result.failedCaseList list_errors = result.errorsCaseList casesort = result.casesort - sys.stderr.write(_Color(fc=38, bo=1, text='结果概要') + "\n") - sys.stderr.write(_Color(fc=37, bo=0, text='总共') + "\x20" + str(case_count["t"]) + "\x20" + - _Color(fc=37, bo=0, text='通过') + "\x20" + str(case_count["p"]) + "\x20" + - _Color(fc=37, bo=0, text='失败') + "\x20" + str(case_count["f"]) + "\x20" + - _Color(fc=37, bo=0, text='错误') + "\x20" + str(case_count["e"]) + "\x20" + - _Color(fc=37, bo=0, text='耗时') + "\x20" + dura + "\x20" + - _Color(fc=37, bo=0, text='通过率') + "\x20" + rate_passed + "\x20" + - _Color(fc=37, bo=0, text='失败率') + "\x20" + rate_failed + "\x20" + - _Color(fc=37, bo=0, text='错误率') + "\x20" + rate_errors + "\x20" + + sys.stderr.write(_Color(fc=36, bo=1, text='结果概要') + "\n") + sys.stderr.write(_Color(fc=37, bo=0, text='总共' + "\x20" + str(case_count["t"]) + "\x20") + + _Color(fc=37, bo=0, text='通过' + "\x20" + str(case_count["p"]) + "\x20") + + _Color(fc=37, bo=0, text='失败' + "\x20" + str(case_count["f"]) + "\x20") + + _Color(fc=37, bo=0, text='错误' + "\x20" + str(case_count["e"]) + "\x20") + + _Color(fc=37, bo=0, text='耗时' + "\x20" + dura + "\x20") + + _Color(fc=37, bo=0, text='通过率' + "\x20" + rate_passed + "\x20") + + _Color(fc=37, bo=0, text='失败率' + "\x20" + rate_failed + "\x20") + + _Color(fc=37, bo=0, text='错误率' + "\x20" + rate_errors + "\x20") + "\n" ) for value in [['失败用例', list_failed, 31], ['错误用例', list_errors, 33]]: @@ -1096,13 +1125,12 @@ class HTMLTestRunner(Template_mixin): else: errorsCase = '无' - return [ - ('测试人员', self.tester), - ('开始时间', runstime), - ('合计耗时', duration), - ('测试结果', status + ',通过率 ' + self.passrate), - ('失败用例', failedCase), - ('错误用例', errorsCase), + return self.testinfo + [ + ['开始时间', runstime], + ['合计耗时', duration], + ['测试结果', status + ',通过率 ' + self.passrate], + ['失败用例', failedCase], + ['错误用例', errorsCase], ] def generateReport(self, test, result): @@ -1110,6 +1138,8 @@ class HTMLTestRunner(Template_mixin): report_count = self._generate_report(result) output = self.HTML_TMPL % dict( title=saxutils.escape(self.title), + logo=self.logo, + sign=self.sign, generator='HTMLTestRunner', styles=self._generate_styles(), passed=report_count["passed"], @@ -1123,7 +1153,7 @@ class HTMLTestRunner(Template_mixin): report=report_count["report"], footer=self._generate_footer(), ) - self.stream.write(output.encode('utf-8')) + self.stream and self.stream.write(output.encode('utf-8')) def _generate_styles(self): return self.STYLES_TMPL @@ -1147,13 +1177,12 @@ class HTMLTestRunner(Template_mixin): "
      " + value + "
    " ) case _: - line = self.HEADER_ATTRIBUTE_TMPL % dict(name=saxutils.escape(name), value=saxutils.escape(value)) + line = self.HEADER_ATTRIBUTE_TMPL % dict(name=saxutils.escape(str(name)), value=saxutils.escape(str(value))) line_list.append(line) return self.HEADER_TMPL % dict( title=saxutils.escape(self.title), parameters=''.join(line_list), - description=saxutils.escape(self.description), - tester=saxutils.escape(self.tester), + description=saxutils.escape(self.description) ) # 生成报告 diff --git a/main.py b/main.py index 3e24907..f615eea 100644 --- a/main.py +++ b/main.py @@ -1,5 +1,3 @@ -import re - - -string = '...\n[TestErrorMsg]./index[/TestErrorMsg]\n[TestErrorMsg]./index[/TestErrorMsg]\n[TestErrorMsg]./index[/TestErrorMsg]\n456\nsddsfgsd: [WinError] 32432532\n[TestErrorMsg]./index[/TestErrorMsg]\n' -print(re.compile('\[[A-Za-z]+].*?\[/[A-Za-z]+][\r\n]').sub('', string)) \ No newline at end of file +a = [] +b = [1,2,3] +print(a + b) \ No newline at end of file diff --git a/main2.py b/main2.py deleted file mode 100644 index c6f504a..0000000 --- a/main2.py +++ /dev/null @@ -1,5 +0,0 @@ -import subprocess,chardet - -runResult = subprocess.run('ping -n 1 www.fanscloud.net && echo 测试',shell=True,stdout=subprocess.PIPE, stderr=subprocess.PIPE) - -print(runResult) diff --git a/main3.py b/main3.py deleted file mode 100644 index e1ef452..0000000 --- a/main3.py +++ /dev/null @@ -1,70 +0,0 @@ -import smtplib -from email.mime.multipart import MIMEMultipart -from email.mime.text import MIMEText -from email.mime.base import MIMEBase -from email.header import Header -from email import encoders - -mail_host = { - "host": 'smtp.qq.com', - "port": 465 -} -mail_user = { - "user": 'admin@fanscloud.net', - "pass": 'gyzzvmjdlofnbedj' -} -sender = "admin@fanscloud.net" - -mailToList = [ - "1634991757@qq.com", - "fanscloud@foxmail.com" -] -mailCcList = [ - "zhaoyafan@foxmail.com" -] -mailBcList = [ - -] -html = ''' -

    Python 邮件发送HTML格式文件测试...

    -

    这是一个链接

    -''' -message = MIMEMultipart() -messageFrom = Header() -messageFrom.append('云凡网络') -messageFrom.append('') -print(messageFrom) -message["From"] = messageFrom -message["To"] = ';'.join(mailToList) -message["Cc"] = ';'.join(mailCcList) -message["Bcc"] = ';'.join(mailBcList) -message['Subject'] = Header('主题333') -message.attach(MIMEText(html, 'html', 'utf-8')) - - -# 构造附件2,传送当前目录下的 runoob.txt 文件 -# att2 = MIMEText(open('./1.xlsx', 'rb').read(), 'base64', 'utf-8') -# att2["Content-Type"] = 'application/octet-stream' -# file = Header('attachment; filename=') -# -# att2["Content-Disposition"] = file -# message.attach(att2) -# -# -# print(file) - -mime = MIMEBase('application','octet-stream') -mime.add_header('Content-Disposition', 'attachment', filename='文件文件.xlsx') -mime.set_payload(open('./1.xlsx', 'rb').read()) -# 用Base64编码: -encoders.encode_base64(mime) -# 添加到MIMEMultipart: -message.attach(mime) - -print("加密后的发送内容\n", message.as_string()) # 打印输出加密后的发送内容 -# -# -smtpObj = smtplib.SMTP_SSL(mail_host['host'], mail_host['port']) -# smtpObj.connect(mail_host['host'], mail_host['port']) # 链接 SMTP 服务器 -smtpObj.login(mail_user['user'], mail_user['pass']) # 登录邮箱验证 -smtpObj.sendmail(sender, mailToList, message.as_string()) # 发送邮件; "message" 通过 "as_string()" 进行发送内容字符串的加密 diff --git a/main4.py b/main4.py deleted file mode 100644 index b108f30..0000000 --- a/main4.py +++ /dev/null @@ -1,81 +0,0 @@ -import requests, logging, time - -def test(): - url = 'http://more-md.fanscloud.net/filebox.php?op=home' - url_upload = 'https://more-me.fanscloud.net/typoraUpload' - - # logging.basicConfig() - # logging.getLogger().setLevel(logging.DEBUG) - # requests_log = logging.getLogger("requests.packages.urllib3") - # - # requests_log.setLevel(logging.DEBUG) - # - # requests_log.propagate = True - - - - - ses = requests.session() - - - req = ses.request( - method='POST', - url=url, - params={"ip": '119.29.29.29'}, - data={"user": 'admin', "pass": '1234ABCDabcd', "submitButtonName": '登录'}, - headers={"Version": '1.0.0', "Connection": 'keep-alive'}, - cookies={}, - files={}, - auth=None, - timeout=10, - allow_redirects=False, - proxies=None, - verify=False, - cert=None, - json=None - ) - req2 = ses.request( - method='GET', - url=url, - params={"ip": '119.29.29.29'}, - data={}, - headers={"Version": '1.0.0', "Connection": 'keep-alive'}, - cookies={}, - files={}, - auth=None, - timeout=10, - allow_redirects=False, - proxies=None, - verify=False, - cert=None, - json=None - ) - # req = requests.post(url=url, params={"ip": '119.29.29.29'}, proxies={"http": '127.0.0.1:8888', "https": '127.0.0.1:8888'}) - # proxies={"http": '127.0.0.1:8888', "https": '127.0.0.1:8888'}, - - - - # print(req.request.method + ' ' + req.request.url + ' ' + 'HTTP/1.1' + '\r\n') - print(req2.text) - # print(req.headers) - # logs = logging.getLogger() - # - # logs.setLevel(logging.DEBUG) - # - # path = './' + time.strftime('%Y-%m-%d-%H-%M-%S') + '.log' - # write_file = logging.FileHandler(path, 'a+', encoding='utf-8') - # - # write_file.setLevel(logging.DEBUG) - # - # set_logs = logging.Formatter('%(asctime)s - %(filename)s - %(funcName)s - %(levelname)s - %(message)s') - # - # write_file.setFormatter(set_logs) - # - # pycharm_text = logging.StreamHandler() - # - # pycharm_text.setFormatter(set_logs) - # - # logs.addHandler(write_file) - # - # logs.addHandler(pycharm_text) -test() \ No newline at end of file diff --git a/main5.py b/main5.py deleted file mode 100644 index d24319b..0000000 --- a/main5.py +++ /dev/null @@ -1,3 +0,0 @@ -from Base.Class.Http import * - -http = Request().http('get', 'http://127.0.0.1:81/iplookup.php', header={"Host": 'fanscloud.net'}, debug=True) \ No newline at end of file diff --git a/main6.py b/main6.py deleted file mode 100644 index 7f1cd09..0000000 --- a/main6.py +++ /dev/null @@ -1,4 +0,0 @@ -from Base.Class.Database import * -sql = MySQL(host='sdm821105491.my3w.com', port=3306, user='sdm821105491', password='1234ABCDabcd', database='sdm821105491_db', charset='utf8', cursor='Dict') -sql.execute('select * from emlog_options where option_name="bloginfo"') -print(sql.fetchall()) \ No newline at end of file diff --git a/main7.py b/main7.py deleted file mode 100644 index d3cde69..0000000 --- a/main7.py +++ /dev/null @@ -1,14 +0,0 @@ -from Base.Class.Excel import * - - -if __name__ == '__main__': - # Example. - # 打开现有Excel文件 - excel = Excel().open(filename='./example.xlsx') - excel.select(2) - # print(excel.cellGetView('A1:C9')) - # print(excel.cellGetView('1:9')) - - print(excel.cellGetView('D5:F12')) - # excel.save() - # print(excel.cellGetView(area='F6:F11')) diff --git a/main8.py b/main8.py deleted file mode 100644 index e69de29..0000000 diff --git a/run.py b/run.py index 57a0daa..19b6f21 100644 --- a/run.py +++ b/run.py @@ -17,7 +17,7 @@ from types import FunctionType obj = type("测试用例",(unittest.TestCase,),dict()) obj.__doc__ = 'Doc' obj.test_demo0123456789ABCDEF_000000 = FunctionType(compile('def foo(self): import time; time.sleep(0)', "", "exec").co_consts[0], globals(), "foo") -obj.test_demo1 = FunctionType(compile('def foo(self): return "bar"', "", "exec").co_consts[0], globals(), "foo") +obj.test_demo1 = FunctionType(compile('def foo(self): return a', "", "exec").co_consts[0], globals(), "foo") obj.test_demo2 = FunctionType(compile('def foo(self): assert 1 == 2', "", "exec").co_consts[0], globals(), "foo") obj.test_demo3 = FunctionType(compile('def foo(): return "bar"', "", "exec").co_consts[0], globals(), "foo") obj.test_demo1.__qualname__ = "test_demo1" @@ -40,6 +40,7 @@ obj.test_demo3.__doc__ = "这是一条测试用例;这是一条测试用例; base_path = os.path.dirname(__file__) report_path = base_path report_filename = os.path.join(report_path, 'report.html') +log_filename = os.path.join(report_path, 'test.log') case_suite = unittest.TestSuite() case_suite.addTest(TestDemo('test_one1')) @@ -63,13 +64,9 @@ case_suite.addTest(TestDemo('test_0123456789ABC')) def start(): - with open(report_filename, 'wb') as f: - runner = HTMLTestRunner(stream=f, - title='自动化测试报告', - verbosity=2, - description='描述', - tester='DESKTOP') - runner.run(case_suite) + runner = HTMLTestRunner(report=report_filename, log=log_filename, stream=None, verbosity=0, title='自动化测试报告', description='描述', info=None) + # runner = HTMLTestRunner() + runner.run(case_suite) if __name__ == '__main__': diff --git a/test_case.py b/test_case.py index f9bd832..b015580 100644 --- a/test_case.py +++ b/test_case.py @@ -1,3 +1,4 @@ +import sys import time import unittest from selenium import webdriver @@ -5,11 +6,11 @@ from selenium.webdriver.chrome.options import Options from Base.Class.Logger import * from Base.Class.Http import * -log = Logger(name='test', level='DEBUG', ch={ - "level": 'DEBUG', - "format": '{asctime} - {name} - {levelname[0]}: {message}' - }) - +# log = Logger(name='test', level='DEBUG', ch={ +# "level": 'DEBUG', +# "format": '{asctime} - {name} - {levelname[0]}: {message}' +# }) +log = Logger(name='test', level='DEBUG') class TestDemo(unittest.TestCase): def test_one1(self): @@ -18,15 +19,20 @@ class TestDemo(unittest.TestCase): # os.system('echo Case One1 Exec...') # time.sleep(5) assert 1 == 1 - # log.i("Info...") - # log.w("Warnning...") + log.i("Info...") + log.w("Warnning...") + print("STDOUT", file=sys.stdout) + print("STDERR", file=sys.stderr) def test_two22(self): time.sleep(0.036) # log.d("正在断言...") + log.e('错误啦!!!') assert 'A' in 'Hello!', '断言失败' def test_tre333(self): + print('[TestErrorImg]./01.png[/TestErrorImg]') + a(1) time.sleep(0.025) # assert a == 10