项目目录
一.实例生成报告(和python2.7配合使用无法生成报告)
+test_pytest_html_one.py
+import pytest
+
+
+def login(username,password):
+ """ 模拟登录 """
+ user = "linux超"
+ pwd = "linux超哥"
+
+ if user == username and pwd == password:
+ return {"code":1001,"msg":"登录成功","data":None}
+ else:
+ return {"code":1000,"msg":"用户名或密码错误","data":None}
+
+
+test_data = [
+ # 测试数据
+ {
+ "case": "用户名正确, 密码正确",
+ "user": "xxx",
+ "pwd": "xxx",
+ "expected": {"code": 1001, "msg": "登录成功", "data": None}
+ },
+ {
+ "case": "用户名正确, 密码为空",
+ "user": "xxx",
+ "pwd": "",
+ "expected": {"code": 1000, "msg": "用户名或密码错误", "data": None}
+ },
+ {
+ "case": "用户名为空, 密码正确",
+ "user": "",
+ "pwd": "xxx",
+ "expected": {"code": 1000, "msg": "用户名或密码错误", "data": None}
+ },
+ {
+ "case": "用户名错误, 密码错误",
+ "user": "x",
+ "pwd": "x",
+ "expected": {"code": 1000, "msg": "用户名或密码错误", "data": None}
+ }
+]
+
+
+# 修改Results:改变输出结果
+ids = [
+ "测试:{}->[用户名:{}-密码:{}-预期:{}]".
+ format(data["case"], data["user"], data["pwd"], data["expected"]) for data in test_data
+]
+
+
+class TestLogin(object):
+ # 添加ids参数
+ @pytest.mark.parametrize("data",test_data,ids=ids)
+ def test_login(self,data):
+ result = login(data["user"],data["pwd"])
+ assert result == data["expected"]
+
+
+if __name__ == "__main__":
+ pytest.main(['-sv',"G:\\pytestbasics\\test_pytest_html_case","--html", "report.html"])
+
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 28
- 29
- 30
- 31
- 32
- 33
- 34
- 35
- 36
- 37
- 38
- 39
- 40
- 41
- 42
- 43
- 44
- 45
- 46
- 47
- 48
- 49
- 50
- 51
- 52
- 53
- 54
- 55
- 56
- 57
- 58
- 59
- 60
1.修改Environment
1.1.在Environment中删除Java_Home,添加测试接口地址该如何添加?
1.2.在项目的根目录新键conftest.py文件。
+conftest.py
+def pytest_configure(config):
+ # 添加接口地址与项目名称
+ config._metadata["项目名称"] = "xxxxxx"
+ config._metadata['接口地址'] = 'xxxxxx'
+ # 删除Java_Home
+ config._metadata.pop("JAVA_HOME")
+
- 1
- 2
- 3
- 4
- 5
- 6
2.修改Summary
2.1.原始的报告中Summary部分只显示了测试用例数及用例执行时间,那么如果想在这个位置添加测试人员等一些信息该如何实现?
conftest.py
+@pytest.mark.optionalhook
+def pytest_html_results_summary(prefix):
+ prefix.extend([html.p("所属部门: xxx")])
+ prefix.extend([html.p("测试人员: xxx")])
+
- 1
- 2
- 3
- 4
3.修改Results:Results主要展示的是测试结果信息,比如Test列显示的内容很长,它主要是测试用例所在的路径及测试类,测试用例及测试数据拼接而成的,可读性很差。所以改成显:测试+用例名称[user:用户名-pwd:密码] 。
4.优化Test
4.1.原始的测试报告的Test列表的信息是怎么生成的。找到pytest-html插件的安装位置,打开plugin.py文件。
class TestResult:
+ def __init__(self, outcome, report, logfile, config):
+ self.test_id = report.nodeid
+
+ if getattr(report, "when", "call") != "call":
+ self.test_id = "::".join([report.nodeid, report.when])
+ self.time = getattr(report, "duration", 0.0)
+ self.outcome = outcome
+
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
4.2.第3行,5行,就是报告中Test信息的来源,test_pytest_html_one.py::TestLogin::test_login[data0] 这样一条信息,实际是用例的nodeid,而【data0】是测试用例参数化时的每个参数。
其中参数化方法@pytest.mark.parametrize(“data”, test_data)的参数ids的作用主要就是用来标记测试用例(第9篇文章),增加测试用例执行后输出信息的可读性,因此可以使用这个参数来改变【data0】,让它显测试数据。
conftest.py
+# 改变输出结果
+ids = [
+ "测试:{}->[用户名:{}-密码:{}-预期:{}]".
+ format(data["case"], data["user"], data["pwd"], data["expected"]) for data in test_data
+]
+
+class TestLogin(object):
+ # 添加ids参数
+ @pytest.mark.parametrize("data", test_data, ids=ids)
+ def test_login(self, data):
+ result = login(data["user"], data["pwd"])
+ assert result == data["expected"]
+
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
4.3.中文部分显示的都是乱码。
4.4.解决中文乱码
+conftest.py
+import pytest
+from py._xmlgen import html
+
+@pytest.mark.hookwrapper
+def pytest_runtest_makereport(item):
+ outcome = yield
+ report = outcome.get_result()
+ getattr(report, 'extra', [])
+ report.nodeid = report.nodeid.encode("utf-8").decode("unicode_escape") # 解决乱码
+
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
5.删除多余部分
5.1.要删除前面多余的部分,查看html报告的页面元素属性。
5.2.使用js代码改变这个元素的innerText。
// 找到Test列中所有的表格元素
+var test_name = document.getElementBysClassName("col-name");
+// 修改第1个表格的innerText属性
+test_name[0].innerText = test_name[0].innerText.split("\[")[1].split(["\]")[0]; // 以[符号分割一次,再以]分割一次,得到目标字符串
+
- 1
- 2
- 3
- 4
5.3.前面的代码只能修改一个表格,如果要修改多个表格只能使用循环。
var case_name_td = document.getElementsByClassName("col-name")
+ for(var i = 0; i < case_name_td.length; i++)
+ try{
+ case_name_td[i].innerText = case_name_td[i].innerText.split("\[")[1].split("\]")[0];
+ }
+ catch(err){
+ // 如果表格中没有[]会抛异常,如果抛异常我就显示null,如果你想显示别的东西自己改吧,因为通常只要我们使用参数化就有[]显示
+ case_name_td[i].innerText = "null";
+ }
+
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
5.4.js代码写好了但是要放在哪里?通过查看报告的html源码发现在html代码的开头引入了这样一些代码。
5.5.在pytest-html插件的源码中发现插件的resource目录下有main.js文件,里面的代码就是上述html代码中的js脚本。
5.6.在main.js中确实存在init()方法,因此把编写的js代码放到init()方法内部。
+function init () {
+ reset_sort_headers();
+
+ add_collapse();
+
+ show_filters();
+
+ toggle_sort_states(find('.initial-sort'));
+
+ find_all('.sortable').forEach(function(elem) {
+ elem.addEventListener("click",
+ function(event) {
+ sort_column(elem);
+ }, false)
+ });
+ // 修改用例报告显示的用例名称
+ var case_name_td = document.getElementsByClassName("col-name")
+ for(var i = 0; i < case_name_td.length; i++)
+ try{
+ case_name_td[i].innerText = case_name_td[i].innerText.split("\[")[1].split("\]")[0];
+ }
+ catch(err){
+ // 如果表格中没有[]会抛异常,如果抛异常我就显示null,如果你想显示别的东西自己改吧,因为通常只要我们使用参数化就有[]显示
+ case_name_td[i].innerText = "null";
+ }
+};
+
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
6.删除Links
conftest.py
@pytest.mark.optionalhook
+def pytest_html_results_table_header(cells):
+ cells.pop(-1) # 删除link列
+
+@pytest.mark.optionalhook
+def pytest_html_results_table_row(report, cells):
+ cells.pop(-1) # 删除link列
+
- 1
- 2
- 3
- 4
- 5
- 6
- 7
7.增加失败截图与用例描述
+https://blog.csdn.net/hyq413950612/article/details/123743418?spm=1001.2014.3001.5502
+https://blog.csdn.net/hyq413950612/article/details/123790898
+二.完整的conftest.py文件
+import pytest
+from selenium import webdriver
+from py._xmlgen import html
+
+_driver = None
+# 测试失败时添加截图和测试用例描述(用例的注释信息)
+
+@pytest.mark.hookwrapper
+def pytest_runtest_makereport(item):
+ """当测试失败的时候,自动截图,展示到html报告中"""
+ pytest_html = item.config.pluginmanager.getplugin('html')
+ outcome = yield
+ report = outcome.get_result()
+ extra = getattr(report, 'extra', [])
+
+ if report.when == 'call' or report.when == "setup":
+ xfail = hasattr(report, 'wasxfail')
+ if (report.skipped and xfail) or (report.failed and not xfail):
+ file_name = report.nodeid.replace("::", "_")+".png"
+ screen_img = _capture_screenshot()
+ if file_name:
+ html = '<div><img src="data:image/png;base64,%s" alt="screenshot" style="width:600px;height:300px;" ' \
+ 'οnclick="window.open(this.src)" align="right"/></div>' % screen_img
+ extra.append(pytest_html.extras.html(html))
+ report.extra = extra
+
+ extra.append(pytest_html.extras.text('some string', name='Different title'))
+ report.description = str(item.function.__doc__)
+ report.nodeid = report.nodeid.encode("utf-8").decode("unicode_escape") # 解决乱码
+
+
+
+def _capture_screenshot():
+ '''截图保存为base64'''
+ return _driver.get_screenshot_as_base64()
+
+
+def pytest_configure(config):
+ # 添加接口地址与项目名称
+ config._metadata["项目名称"] = "Linux超博客园自动化测试项目v1.0"
+ config._metadata['接口地址'] = 'https://www.cnblogs.com/linuxchao/'
+ # 删除Java_Home
+ config._metadata.pop("JAVA_HOME")
+
+
+@pytest.mark.optionalhook
+def pytest_html_results_summary(prefix):
+ prefix.extend([html.p("所属部门: xx测试中心")])
+ prefix.extend([html.p("测试人员: Linux超")])
+
+
+@pytest.mark.optionalhook
+def pytest_html_results_table_header(cells):
+ cells.insert(1, html.th('Description'))
+ cells.pop(-1) # 删除link列
+
+
+@pytest.mark.optionalhook
+def pytest_html_results_table_row(report, cells):
+ cells.insert(1, html.td(report.description))
+ cells.pop(-1) # 删除link列
+
+
+def pytest_configure(config):
+ # 添加接口地址与项目名称
+ config._metadata["项目名称"] = "xxxxx"
+ config._metadata['接口地址'] = 'xxxxx'
+ # 删除Java_Home
+ config._metadata.pop("JAVA_HOME")
+
+
+@pytest.fixture(scope='module') #或者改成session
+def driver():
+ global _driver
+ _driver = webdriver.Chrome()
+
+ yield _driver
+ _driver.quit()
+
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 28
- 29
- 30
- 31
- 32
- 33
- 34
- 35
- 36
- 37
- 38
- 39
- 40
- 41
- 42
- 43
- 44
- 45
- 46
- 47
- 48
- 49
- 50
- 51
- 52
- 53
- 54
- 55
- 56
- 57
- 58
- 59
- 60
- 61
- 62
- 63
- 64
- 65
- 66
- 67
- 68
- 69
- 70
- 71
- 72
- 73
- 74
- 75
- 76
- 77
- 78
三.汉化报告
1.修改plugin.py文件主要用来生成测试报告的html代码。
# if len(log) == 0:
+# log = html.div(class_='empty log')
+# log.append('No log output captured.')
+if len(log) == 0:
+ log = html.div(class_='empty log')
+ log.append('未捕获到日志')
+additional_html.append(log)
+
- 1
- 2
- 3
- 4
- 5
- 6
- 7
# head = html.head(
+# html.meta(charset='utf-8'),
+# html.title('Test Report'),
+# html_css)
+head = html.head(
+ html.meta(charset='utf-8'),
+ html.title('测试报告'),
+ html_css)
+
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
# outcomes = [Outcome('passed', self.passed),
+# Outcome('skipped', self.skipped),
+# Outcome('failed', self.failed),
+# Outcome('error', self.errors, label='errors'),
+# Outcome('xfailed', self.xfailed,
+# label='expected failures'),
+# Outcome('xpassed', self.xpassed,
+# label='unexpected passes')]
+outcomes = [Outcome('passed', self.passed, label="通过"),
+ Outcome('skipped', self.skipped, label="跳过"),
+ Outcome('failed', self.failed, label="失败"),
+ Outcome('error', self.errors, label='错误'),
+ Outcome('xfailed', self.xfailed,
+ label='预期失败'),
+ Outcome('xpassed', self.xpassed,
+ label='预期通过')]
+
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
# if self.rerun is not None:
+# outcomes.append(Outcome('rerun', self.rerun))
+if self.rerun is not None:
+ outcomes.append(Outcome('重跑', self.rerun))
+
+# summary = [html.p(
+# '{0} tests ran in {1:.2f} seconds. '.format(
+# numtests, suite_time_delta)),
+# html.p('sfsf',
+# class_='filter',
+# hidden='true')]
+summary = [html.p(
+ '执行了{0}个测试用例, 历时:{1:.2f}秒 . '.format(
+ numtests, suite_time_delta)),
+ html.p('(取消)勾选复选框, 以便筛选测试结果',
+ class_='filter',
+ hidden='true')]
+
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
# cells = [
+# html.th('Result',
+# class_='sortable result initial-sort',
+# col='result'),
+# html.th('Test', class_='sortable', col='name'),
+# html.th('Duration', class_='sortable numeric', col='duration'),
+# html.th('Links')]
+cells = [
+ html.th('通过/失败',
+ class_='sortable result initial-sort',
+ col='result'),
+ html.th('用例', class_='sortable', col='name'),
+ html.th('耗时', class_='sortable numeric', col='duration'),
+ html.th('链接')]
+
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
# results = [html.h2('Results'), html.table([html.thead(
+# html.tr(cells),
+# html.tr([
+# html.th('No results found. Try to check the filters',
+# colspan=len(cells))],
+# id='not-found-message', hidden='true'),
+# id='results-table-head'),
+# self.test_logs], id='results-table')]
+results = [html.h2('测试结果'), html.table([html.thead(
+ html.tr(cells),
+ html.tr([
+ html.th('无测试结果, 试着选择其他测试结果条件',
+ colspan=len(cells))],
+ id='not-found-message', hidden='true'),
+ id='results-table-head'),
+ self.test_logs], id='results-table')]
+
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
#html.p('Report generated on {0} at {1} by '.format(
+html.p('生成报告时间{0} {1} Pytest-Html版本:'.format(
+
- 1
- 2
# body.extend([html.h2('Summary')] + summary_prefix
+# + summary + summary_postfix)
+body.extend([html.h2('用例统计')] + summary_prefix
+ + summary + summary_postfix)
+
- 1
- 2
- 3
- 4
# environment = [html.h2('Environment')]
+environment = [html.h2('测试环境')]
+
- 1
- 2
2.修改main.js文件。
+/*
+function add_collapse() {
+ // Add links for show/hide all
+ var resulttable = find('table#results-table');
+ var showhideall = document.createElement("p");
+ showhideall.innerHTML = '<a href="javascript:show_all_extras()">Show all details</a> / ' +
+ '<a href="javascript:hide_all_extras()">Hide all details</a>';
+ resulttable.parentElement.insertBefore(showhideall, resulttable);
+*/
+function add_collapse() {
+ // Add links for show/hide all
+ var resulttable = find('table#results-table');
+ var showhideall = document.createElement("p");
+ showhideall.innerHTML = '<a href="javascript:show_all_extras()">显示详情</a> / ' +
+ '<a href="javascript:hide_all_extras()">隐藏详情</a>';
+ resulttable.parentElement.insertBefore(showhideall, resulttable);
+
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
3.修改style.css文件主要用来美化测试报告的。
+.expander::after {
+ content: " (展开详情)";
+ color: #BBB;
+ font-style: italic;
+ cursor: pointer;
+}
+.collapser::after {
+ content: " (隐藏详情)";
+ color: #BBB;
+ font-style: italic;
+ cursor: pointer;
+}
+
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
四.安装汉化版插件
+1.汉化版的pytest-html插件源码上传到GitHub。
https://github.com/13691579846/pytest-html
2.方法一:
+2.1.如果已经安装过pytest-html,先卸载 pip uninstall pytest-html
+2.2.下载插件源码到本地
git clone https://github.com/13691579846/pytest-html
2.3.打开CMD切换到插件中setup.py文件所在目录,执行命令python setup.py install
3.方法二:
+3.1.如果未安装过pytest-html插件,请先执行如下命令安装
pip install pytest-html
3.2.按照方法1的方式下载汉化后的插件,把下载后的插件中的部分附件覆盖到第三方库目录下pytest-html下的部分文件
4.方法三:
+4.1.直接下载汉化后的源码,把源码中的pytest_html直接丢到python第三方库存放目录即可。
+