"""
这是一个用于unittest框架的TestRunner,用它可以生成HTML测试报告,包含用例执行情况以
及图表;
使用它最简单方法是调用main方法,例如:
------------------------------------------------------------------------
# import unittest
# import HTMLTestRunner
# 在这里,
# 定义你的测试代码。
# if __name__ == '__main__':
# HTMLTestRunner.main()
------------------------------------------------------------------------
"""
import os, re, sys, io, time, datetime, platform, json, unittest, logging, shutil
from xml.sax import saxutils
class OutputRedirector(object):
"""
重定向stdout和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)
class ReportTemplate:
"""
报告模板
"""
STATUS = {
0: '通过',
1: '失败',
2: '错误',
}
DEFAULT_TITLE = '自动化测试报告'
DEFAULT_DESCRIPTION = '暂无描述'
# ------------------------------------------------------------------------
# 网页模板
# 变量列表 title, generator, styles, header, report, footer
HTML_TMPL = """
%(title)s
%(styles)s
%(header)s
%(report)s
%(footer)s
"""
# ------------------------------------------------------------------------
# 网页样式
STYLES_TMPL = """
"""
# ------------------------------------------------------------------------
# 报告标题、测试信息、报告描述、截图显示容器、图表容器
# 变量列表 title, parameters, description, logo, sign
HEADER_TMPL = """
%(sign)s
"""
# ------------------------------------------------------------------------
# 测试信息
# 变量列表 name, value
HEADER_ATTRIBUTE_TMPL = """
%(name)s : %(value)s
"""
# ------------------------------------------------------------------------
# 报告模板
# 变量列表 test_list, counts, passed, failed, errors ,passrate
REPORT_TMPL = """
%(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 |
|
截图信息
%(screenshot)s
|
"""
# ------------------------------------------------------------------------
# 失败样式(无截图列)
# 变量列表 tid, Class, style, desc, status
REPORT_TEST_WITH_OUTPUT_TMPL_0 = """
%(name)s |
%(docs)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='', data=''):
return re.findall('\\[' + mark + '](.*?)\\[/' + mark + ']' + '*?', data)
def _makeMark(mark='', cont=''):
return '[' + mark + ']' + cont + '[/' + mark + ']'
def _Color(fc=0, bc=0, bo=0, text=''):
b = 'PYCHARM_HOSTED' in os.environ.keys()
return "\033[" + str(bo) + ['', ';' + str(fc)][fc > 0] + ['', ';' + str(bc)][bc > 0] + "m" + text + "\033[0m" if b else text
class _TestResult(unittest.TestResult):
def __init__(self, verbosity=1, log='', success_log_in_report=False):
super().__init__(verbosity=verbosity)
self.verbosity = verbosity
self.log = log
self.success_log_in_report = success_log_in_report
self.fh = None
self.lh = None
self.ch = None
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
self.passrate = float(0)
# 分类统计数量耗时
self.casesort = {}
# 增加失败用例合集
self.failedCase = ''
self.failedCaseList = []
# 增加错误用例合集
self.errorsCase = ''
self.errorsCaseList = []
self.logger = logging.getLogger('test')
# result is a list of result in 4 tuple:
# 1. result code (0: passed; 1: failed; 2: errors),
# 2. testcase object,
# 3. test output (byte string),
# 4. stack trace.
self.result = []
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 >= 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 True:
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.log:
self.fh = logging.FileHandler(filename=self.log, 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 completeOutput(self):
"""
单条用例执行结束后,添加结果前的动作;
添加结果需要调用的方法;
断开输出重定向;
返回日志和输出;
"""
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)
# 移除日志Handler
for handler in [self.fh, self.lh, self.ch]:
if handler:
self.logger.removeHandler(handler)
def singleEndConsolePrint(self, test, term_mark, term_head, term_clor, duration):
"""
将用例执行结果打印到终端显示
"""
self.verbosity >= 1 and 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(duration).strftime('%H:%M:%S.%f')[0:12]),
_Color(fc=37, bo=0, 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=37, bo=0, text='<='),
_Color(fc=37, bo=0, text=str(test.__dict__['_testMethodDoc'] and test.__dict__['_testMethodDoc'].strip().splitlines()[0].strip() or ''))
))
def addSuccess(self, test):
"""
单条用例执行结束后添加成功结果动作
"""
term_mark, term_head, term_clor = '=', 'Passed', 32
self.passed_count += 1
super().addSuccess(test)
output = self.completeOutput()
duration = round(self.etime - self.stime, 3)
self.sortCount(cls=test.__class__.__qualname__, res=term_head, dur=duration)
# 添加测试结果
self.result.append((0, test, ['', output[0]][bool(self.success_log_in_report)] + output[1], '', duration))
# 单条用例执行结束后在终端打印结果
self.singleEndConsolePrint(test, term_mark, term_head, term_clor, duration)
def addError(self, test, err):
"""
单条用例执行结束后添加错误结果动作
"""
term_mark, term_head, term_clor = '?', 'Errors', 33
self.errors_count += 1
super().addError(test, err)
_, _exc_str = self.errors[-1]
output = self.completeOutput()
duration = round(self.etime - self.stime, 3)
self.sortCount(cls=test.__class__.__qualname__, res=term_head, dur=duration)
# 添加测试结果
self.result.append((2, test, output[0] + output[1], _exc_str, duration))
# 用例执行结束后在终端打印结果
self.singleEndConsolePrint(test, term_mark, term_head, term_clor, duration)
# 收集错误测试用例名称以在测试报告中显示
testcase_name = '%s.%s.%s' % (str(test.__module__).strip('_'), test.__class__.__qualname__, test.__dict__['_testMethodName'])
self.errorsCase += '%s' % testcase_name
self.errorsCaseList.append(testcase_name)
def addFailure(self, test, err):
"""
单条用例执行结束后添加失败结果动作
"""
term_mark, term_head, term_clor = '!', 'Failed', 31
self.failed_count += 1
super().addFailure(test, err)
_, _exc_str = self.failures[-1]
output = self.completeOutput()
duration = round(self.etime - self.stime, 3)
self.sortCount(cls=test.__class__.__qualname__, res=term_head, dur=duration)
# 添加测试结果
self.result.append((1, test, output[0] + output[1], _exc_str, duration))
# 用例执行结束后在终端打印结果
self.singleEndConsolePrint(test, term_mark, term_head, term_clor, duration)
# 收集失败测试用例名称以在测试报告中显示
testcase_name = '%s.%s.%s' % (str(test.__module__).strip('_'), test.__class__.__qualname__, test.__dict__['_testMethodName'])
self.failedCase += '%s' % testcase_name
self.failedCaseList.append(testcase_name)
class HTMLTestRunner(ReportTemplate):
def __init__(
self,
stream=None,
verbosity: int = 1,
title: str = None,
description: str = None,
success_log_in_report=False,
report_home: str = None,
report_home_latest_name: str = None,
report: str = None,
log: str = None,
logo='',
sign=''
):
"""
This is HTMLTestRunner.
:param stream: HTML report write file stream.
:param verbosity:
0: Terminal only shows the test results summary.
1: Terminal shows the results summary and results of each case test.
2: Terminal shows the results summary and results of each case test, and print log.
:param title: Title of report.
:param description: Description of report.
:param success_log_in_report: The testcase passed is also displayed in the HTML report.
:param report_home:
Set home directory of the report for this test,
all the results related files will be storage inside(including HTML report, log, images),
at this time,
other related parameters will fail.
:param report_home_latest_name: Create directory on the path where the report directory is located.
:param report: HTML report file location(useless when stream parameter inset).
:param log: Log file.
:param logo: Logo of report, pass an image URL.
:param sign: Sign of report, Pass in one piece short text.
"""
if report_home:
try:
os.mkdir(report_home)
except Exception:
raise Exception('Report directory already exists.')
stream = report = log = None
report, log = '%s/index.html' % report_home, '%s/HTMLTestRunner.log' % report_home
self.report_home = report_home
self.result_associate_files = []
self.verbosity = verbosity
self.success_log_in_report = success_log_in_report
self.report_home_latest_name = report_home_latest_name
self.stream = stream or (report and open(os.path.abspath(report), 'wb'))
self.report = None
if self.stream:
stream_name = os.path.abspath(self.stream.name)
self.result_associate_files.append(stream_name)
os.environ['HTML_REPORT_ROOT'] = os.path.dirname(stream_name)
self.report = stream_name
else:
os.environ['HTML_REPORT_ROOT'] = ''
self.log = log
self.log_location = log and os.path.abspath(log)
self.log_location and self.result_associate_files.append(self.log_location)
self.log_location and open(self.log_location, 'wb').close()
self.title = self.DEFAULT_TITLE if title is None else title
self.description = self.DEFAULT_DESCRIPTION if description is None else description
self.logo = logo or ''
self.sign = sign or ''
self.passrate = None
self.errormsg = None
self.runstime = None
self.runetime = None
def run(self, test):
sys.stderr.write(_Color(fc=38, bo=1, text='* * * * * * * * * * * * * * * * * * 开始测试 * * * * * * * * * * * * * * * * * *') + '\n')
self.verbosity >= 2 and sys.stderr.write("\n")
self.runstime = round(time.time(), 3)
# 获取测试结果
result = _TestResult(verbosity=self.verbosity, log=self.log_location, success_log_in_report=self.success_log_in_report)
test(result)
self.runetime = round(time.time(), 3)
self.verbosity >= 2 and sys.stderr.write("\n")
sys.stderr.write(_Color(fc=38, bo=1, text='* * * * * * * * * * * * * * * * * * 结束测试 * * * * * * * * * * * * * * * * * *') + '\n')
# 生成测试报告
self.generateReport(test, result)
if len(result.result) == 0:
raise Exception('No testcases found.')
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='%-22s' % ('用例集合') + "\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='%-22s' % (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" +
'%-8s' % ('%.3f' % (round(value["d"], 3)) + '秒') + "\t") + "\n")
return result
@staticmethod
def sortResult(result_list):
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):
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 = '无'
uname = platform.uname()
return [
['开始时间', runstime],
['合计耗时', duration],
['主机名称', '%s (%s %s) %s' % (uname.node, uname.system, uname.release, uname.machine.lower())],
['测试结果', '%s,通过率%s' % (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),
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'))
self.stream and self.stream.close()
if self.report_home and self.report_home_latest_name:
latest = '%s/%s' % (os.path.dirname(self.report_home), self.report_home_latest_name)
os.path.exists(latest) and shutil.rmtree(latest)
shutil.copytree(self.report_home, latest)
if self.report_home:
open('%s/latest.ini' % os.path.dirname(self.report_home), mode='w').write(os.path.basename(self.report_home))
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(
rail_hidden='none' if not self.logo and not self.sign else 'flex',
logo=self.logo,
sign=self.sign,
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 'errorsClass' or nf > 0 and 'failedClass' or 'passedClass',
name=cls.__qualname__,
docs=cls.__doc__ and cls.__doc__.strip().splitlines()[0].strip() 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):
hasout = bool(o or e)
match n:
case 0:
tid_flag = 'p'
pre_clor = '#119611'
case 1:
tid_flag = 'f'
pre_clor = '#e52000'
case 2:
tid_flag = 'e'
pre_clor = '#e54f00'
case _:
tid_flag = 'u'
pre_clor = '#808080'
# ID修改点为下划线;
# 支持Bootstrap折叠展开特效;
# 例如:'pt1_1', 'ft1_1', 'et1_1'
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)))
# 截图名称通过抛出异常在
# 标准错误当中,
# 判断是否包含截图信息
# 实际检测输出当中是否包含report-screenshot关键字;
output = uo + ue
self.errormsg = output.find('report-screenshot')
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],
pre_color=pre_clor
)
else:
# 包含截图信息
template = hasout and self.REPORT_TEST_WITH_OUTPUT_TMPL_1 or self.REPORT_TEST_NO_OUTPUT_TMPL
screenshot_list = _findMark(mark='report-screenshot', data=output)
screenshot = ''
for image in screenshot_list:
bn = os.path.basename(image)
abs_image = os.path.abspath(image)
self.result_associate_files.append(abs_image)
# 移动图片位置到报告所在目录
try:
self.report and shutil.move(abs_image, '%s/%s' % (os.path.dirname(self.report), bn))
except Exception:
pass
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],
pre_color=pre_clor,
screenshot=screenshot
)
return row
def _generate_footer(self):
return self.FOOTER_TMPL