基于nose-html-report定制测试报告

环境准备
pip3.5 install nose-html-report
安装目录文件:
├── __init__.py├── __pycache__│ └── __init__.cpython-35.pyc└── templates├── report2.jinja2└── report.html
主要修改 __init__.py文件及测试报告模板文件report2.jinja2
jinja2模板有自己的语法,具体可以谷歌,如:
if elif else endif 条件语法 :
{% if not test.failed %}{% set test_status=pass %}{% elif test.failed and test.type == 'skipped' %}{% set test_status=skip %}{% elif test.failed and test.type != 'skipped' %}{% set test_status=fail %}{%- else -%}{% set test_status=error %}{%- endif %}
for语法:
l{% for test in group.tests %}
{%- endfor -%}
测试报告定制的实现主要包括如下类
class outputredirector(object): # 输出重定向类
包含一个文件对象fp,主要包括如下方法
def write(self, s): def writelines(self, lines):def flush(self):def readall(self):
class group(object): # 测试用例组
主要包括如下两个对象
self.stats = {'errors': 0, 'failures': 0, 'passes': 0, 'skipped': 0} # 用例组的结果状态统计self.tests = [] # 用例组中具体测试用例统计
class htmlreport(plugin): # 测试报告插件
构造函数def starttest(self, test): # 重定向输出初始化def complete_test_output(self, err_msg='', traceback=''): # 测试完成后的断开重定向链接同时返回缓存数据def begin(self):def complete_global_output(self):def options(self, parser, env): 通过nosetests运行测试用例时携带的命令选项 def configure(self, options, config): 通过命令配置进行实际相关配置两边的加载 根据配置加载指定模板def report(self, stream): 基于模板,对模板中的变量进行赋值,写测试报告 通过self.report_data,以group为单位,对测试结果结果进行传递,group中包含stats及tests对象,分别记录该测试组中测试运行状态及具体用例信息writes an xunit-formatted xml filethe file includes a report of test errors and failures.self.stats['total'] = sum(self.stats.values())for group in self.report_data.values(): group.stats['total'] = sum(group.stats.values())self.report_file.write(self.jinja.get_template(os.path.basename(self.report_template_filename)).render( report=self.report_data, stats=self.stats, start_time = str(self.test_start_time)[:19], rawoutput=self._format_output(selfplete_global_output()), duration_time = self.test_duration_time - datetime.resolution))self.report_file.close()if self.config.verbosity > 1: stream.writeln(- * 70) stream.writeln(html: %s % self.report_file.name)def addsuccess(self, test): # 测试成功 返回的状态信息及用例详情name = id_split(test.id())group = self.report_data[name[0]]self.stats['passes'] += 1group.stats['passes'] += 1group.tests.append({ 'name': name[-1], 'failed': false, 'output': self._format_output(selfplete_test_output()), 'shortdescription': test.shortdescription(), 'time': str(datetime.now() - self.test_start_time),})def adderror(self, test, err, capt=none): # 测试失败 返回的状态信息及用例详情、错误信息等add error output to xunit report.exc_type, exc_val, tb = errtb = ''.join(traceback.format_exception( exc_type, exc_val if isinstance(exc_val, exc_type) else exc_type(exc_val), tb))name = id_split(test.id())group = self.report_data[name[0]]if issubclass(err[0], skiptest): type = 'skipped' self.stats['skipped'] += 1 group.stats['skipped'] += 1else: type = 'error' self.stats['errors'] += 1 group.stats['errors'] += 1group.tests.append({ 'name': name[-1], 'failed': true, 'type': type, 'errtype': nice_classname(err[0]), 'message': exc_message(err), 'tb': self._format_output(tb), 'output': self._format_output(selfplete_test_output(exc_message(err), tb)), 'shortdescription': test.shortdescription(), 'time': str(datetime.now() - self.test_start_time),})def addfailure(self, test, err, capt=none): # 测试失败 返回的状态信息及用例详情、失败详情add failure output to xunit report.exc_type, exc_val, tb = errtb = ''.join(traceback.format_exception( exc_type, exc_val if isinstance(exc_val, exc_type) else exc_type(exc_val), tb))name = id_split(test.id())group = self.report_data[name[0]]self.stats['failures'] += 1group.stats['failures'] += 1group.tests.append({ 'name': name[-1], 'failed': true, 'errtype': nice_classname(err[0]), 'message': exc_message(err), 'tb': self._format_output(tb), 'output': self._format_output(selfplete_test_output(exc_message(err), tb)), 'shortdescription': test.shortdescription(), 'time': str(datetime.now() - self.test_start_time),})def _format_output(self, o): 字符相关编码
if isinstance(o, str): # return o.decode('latin-1') return o.decode('utf-8') else: return o