diff --git a/python3x/HTMLTestReportCN.py b/python3x/HTMLTestReportCN.py index 6b20537..d7227c8 100644 --- a/python3x/HTMLTestReportCN.py +++ b/python3x/HTMLTestReportCN.py @@ -107,6 +107,7 @@ import unittest from xml.sax import saxutils import sys +import re # ------------------------------------------------------------------------ # The redirectors below are used to capture output during testing. Output @@ -185,9 +186,9 @@ class Template_mixin(object): 2: '错误', } - DEFAULT_TITLE = '单元测试报告' + DEFAULT_TITLE = '测试报告' DEFAULT_DESCRIPTION = '' - DEFAULT_TESTER='最棒QA' + DEFAULT_TESTER='QA' # ------------------------------------------------------------------------ # HTML Template @@ -213,6 +214,7 @@ class Template_mixin(object): 1:Failed //pt hiddenRow, ft none 2:Pass //pt none, ft hiddenRow 3:All //pt none, ft none +4:Error //pt diddenRow, et none */ function showCase(level) { trs = document.getElementsByTagName("tr"); @@ -220,7 +222,7 @@ class Template_mixin(object): tr = trs[i]; id = tr.id; if (id.substr(0,2) == 'ft') { - if (level == 2 || level == 0 ) { + if (level == 2 || level == 0 || level ==4) { tr.className = 'hiddenRow'; } else { @@ -228,7 +230,15 @@ class Template_mixin(object): } } if (id.substr(0,2) == 'pt') { - if (level < 2) { + if (level == 1 || level == 0 || level == 4) { + tr.className = 'hiddenRow'; + } + else { + tr.className = ''; + } + } + if (id.substr(0,2) == 'et') { + if (level < 4) { tr.className = 'hiddenRow'; } else { @@ -263,6 +273,10 @@ class Template_mixin(object): if (!tr) { tid = 'p' + tid0; tr = document.getElementById(tid); + if (!tr) { + tid = 'e' + tid0; + tr = document.getElementById(tid); + } } id_list[i] = tid; if (tr.className) { @@ -356,8 +370,9 @@ class Template_mixin(object): REPORT_TMPL = """

概要{ %(passrate)s } -失败{ %(fail)s } 通过{ %(Pass)s } +失败{ %(fail)s } +错误{ %(error)s } 所有{ %(count)s }

@@ -375,6 +390,7 @@ class Template_mixin(object): + %(test_list)s @@ -384,6 +400,7 @@ class Template_mixin(object): +
通过 失败 错误耗时 详细
%(Pass)s %(fail)s %(error)s%(time_usage)s 通过率:%(passrate)s
@@ -396,6 +413,7 @@ class Template_mixin(object): %(Pass)s %(fail)s %(error)s + %(time_usage)s 详细 """ # variables: (style, desc, count, Pass, fail, error, cid) @@ -412,7 +430,7 @@ class Template_mixin(object):
-
+    
     %(script)s
     
@@ -473,6 +491,11 @@ def __init__(self, verbosity=1): def startTest(self, test): + stream = sys.stderr + # stdout_content = " Testing: " + str(test) + # stream.write(stdout_content) + # stream.flush() + # stream.write("\n") TestResult.startTest(self, test) # just one buffer for both stdout and stderr self.outputBuffer = io.StringIO() @@ -482,13 +505,14 @@ def startTest(self, test): self.stderr0 = sys.stderr sys.stdout = stdout_redirector sys.stderr = stderr_redirector - + self.test_start_time = round(time.time(), 2) def complete_output(self): """ Disconnect output redirection and return buffer. Safe to call multiple times. """ + self.test_end_time = round(time.time(), 2) if self.stdout0: sys.stdout = self.stdout0 sys.stderr = self.stderr0 @@ -508,45 +532,51 @@ def addSuccess(self, test): self.success_count += 1 TestResult.addSuccess(self, test) output = self.complete_output() - self.result.append((0, test, output, '')) + use_time = round(self.test_end_time - self.test_start_time, 2) + self.result.append((0, test, output, '', use_time)) if self.verbosity > 1: - sys.stderr.write('ok ') + sys.stderr.write(' S ') sys.stderr.write(str(test)) sys.stderr.write('\n') else: - sys.stderr.write('.') + sys.stderr.write(' S ') + sys.stderr.write('\n') 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)) + use_time = round(self.test_end_time - self.test_start_time, 2) + self.result.append((2, test, output, _exc_str, use_time)) if self.verbosity > 1: - sys.stderr.write('E ') + sys.stderr.write(' E ') sys.stderr.write(str(test)) sys.stderr.write('\n') else: - sys.stderr.write('E') + sys.stderr.write(' E ') + sys.stderr.write('\n') 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)) + use_time = round(self.test_end_time - self.test_start_time, 2) + self.result.append((1, test, output, _exc_str, use_time)) if self.verbosity > 1: - sys.stderr.write('F ') + sys.stderr.write(' F ') sys.stderr.write(str(test)) sys.stderr.write('\n') else: - sys.stderr.write('F') + sys.stderr.write(' F ') + sys.stderr.write('\n') class HTMLTestRunner(Template_mixin): """ """ - def __init__(self, stream=sys.stdout, verbosity=1,title=None,description=None,tester=None): + def __init__(self, stream=sys.stdout, verbosity=2 ,title=None,description=None,tester=None): self.stream = stream self.verbosity = verbosity if title is None: @@ -567,7 +597,7 @@ def __init__(self, stream=sys.stdout, verbosity=1,title=None,description=None,te def run(self, test): "Run the given test case or test suite." - result = _TestResult(self.verbosity) + result = _TestResult(self.verbosity) # verbosity为1,只输出成功与否,为2会输出用例名称 test(result) self.stopTime = datetime.datetime.now() self.generateReport(test, result) @@ -580,12 +610,12 @@ def sortResult(self, result_list): # Here at least we want to group them together by class. rmap = {} classes = [] - for n,t,o,e in result_list: + 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)) + rmap[cls].append((n,t,o,e,s)) r = [(cls, rmap[cls]) for cls in classes] return r @@ -604,17 +634,36 @@ def getReportAttributes(self, result): if result.error_count: status.append('错误 %s' % result.error_count ) if status: status = ','.join(status) - self.passrate = str("%.2f%%" % (float(result.success_count) / float(result.success_count + result.failure_count + result.error_count) * 100)) + if (result.success_count + result.failure_count + result.error_count) > 0: + self.passrate = str("%.2f%%" % (float(result.success_count) / float(result.success_count + result.failure_count + result.error_count) * 100)) + else: + self.passrate = "0.00 %" else: status = 'none' + # 失败模块列表统计逻辑start + # 为了发邮件的时候确定知道哪个模块失败了,这里在报告的头里面单独添加一行失败模块的列表 + # 把用例结果和对应的模块名称单独提取到一个list + module_result_list = [(status, re.split(" |\(|\.", str(name))[2]) for status, name, _, _, _ in result.result] + failed_report_module_list = [] # 初始化模块失败列表 + for module_result in module_result_list: + if module_result[0] != 0: # 如果结果是失败的,则把模块名字放到失败模块列表 + failed_report_module_list.append(module_result[1]) + failed_report_module_list = list(set(failed_report_module_list)) # 去重 + if len(failed_report_module_list) == 0: # 如果没有失败的模块,失败模块日志为固定描述,否则用“,”连接 + failed_string = "无失败模块" + elif len(failed_report_module_list) == 1: + failed_string = "".join(failed_report_module_list) + else: + failed_string = ",".join(failed_report_module_list) + # 失败模块列表统计逻辑end return [ ('测试人员', self.tester), ('开始时间',startTime), ('合计耗时',duration), - ('测试结果',status + ",通过率= "+self.passrate), + ('测试结果',status + ",通过率 = "+self.passrate), + ('失败的模块', failed_string) ] - def generateReport(self, test, result): report_attrs = self.getReportAttributes(result) generator = 'HTMLTestRunner %s' % __version__ @@ -657,14 +706,18 @@ def _generate_heading(self, report_attrs): def _generate_report(self, result): rows = [] sortedResult = self.sortResult(result.result) + # 所有用例统计耗时初始化 + sum_ns = 0 for cid, (cls, cls_results) in enumerate(sortedResult): # subtotal for a class - np = nf = ne = 0 - for n,t,o,e in cls_results: + np = nf = ne = ns = 0 + for n,t,o,e,s in cls_results: if n == 0: np += 1 elif n == 1: nf += 1 - else: ne += 1 - + elif n == 2: ne += 1 + ns += s # 把单个class用例文件里面的多个def用例每次的耗时相加 + ns = round(ns, 2) + sum_ns += ns # 把所有用例的每次耗时相加 # format class description if cls.__module__ == "__main__": name = cls.__name__ @@ -681,28 +734,36 @@ def _generate_report(self, result): fail = nf, error = ne, cid = 'c%s' % (cid+1), + time_usage=str(ns) + "秒" # 单个用例耗时 ) rows.append(row) - for tid, (n,t,o,e) in enumerate(cls_results): + for tid, (n,t,o,e,s) in enumerate(cls_results): self._generate_report_test(rows, cid, tid, n, t, o, e) - + sum_ns = round(sum_ns, 2) 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), + time_usage=str(sum_ns) + "秒", # 所有用例耗时 passrate =self.passrate, ) return report def _generate_report_test(self, rows, cid, tid, n, t, o, e): - # e.g. 'pt1.1', 'ft1.1', etc + # e.g. 'pt1_1', 'ft1_1', 'et1_1'etc has_output = bool(o or e) # ID修改点为下划线,支持Bootstrap折叠展开特效 - Findyou - tid = (n == 0 and 'p' or 'f') + 't%s_%s' % (cid+1,tid+1) + if n == 0: + tid_flag = 'p' + elif n == 1: + tid_flag = 'f' + elif n == 2: + tid_flag = 'e' + tid = tid_flag + '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