批量执行完用例后,生成的测试报告是文本形式的,不够直观,为了更好的展示测试报告,最好是生成HTML格式的。

unittest里面是不能生成html格式报告的,需要导入一个第三方的模块:HTMLTestRunner

 

一、导入HTMLTestRunner

1.这个模块下载不能通过pip安装了,只能下载后手动导入,下载地址:http://tungwaiyip.info/software/HTMLTestRunner.html

Selenium3+python自动化011-unittest生成测试报告(HTMLTestRunner)_html

2.Download下HTMLTestRunner.py文件就是我们需要下载的包。

3.下载后手动拖到python安装文件的Lib目录下

4.验证安装成功 执行 import HTMLTestRunner 没有报错证明配置成功。

Selenium3+python自动化011-unittest生成测试报告(HTMLTestRunner)_javascript_02

 

报告路径

Selenium3+python自动化011-unittest生成测试报告(HTMLTestRunner)_html_03

 

Selenium3+python自动化011-unittest生成测试报告(HTMLTestRunner)_html_04

Selenium3+python自动化011-unittest生成测试报告(HTMLTestRunner)_html_05

1 # encoding = utf - 8
2 import unittest
3 import sys
4 import HTMLTestRunner
5 import time
6 import os
7
8 #被测试类
9 class myclass(object):
10 """classmethod 修饰符对应的函数不需要实例化,
11 不需要 self 参数,但第一个参数需要是表示自身
12 类的 cls 参数,可以来调用类的属性,类的方法,
13 实例化对象等。"""
14 @classmethod
15 def sum(cls,a,b):
16 return a + b #将两个传入参数进行相加操作
17 @classmethod
18 def sub(cls, a, b):
19 return a - b #将两个传入参数进行相减操作
20
21 class mytest(unittest.TestCase):
22 # @classmethod
23 # def setUpClass(cls):
24 # """初始化类固件"""
25 # print("-----setUpClass")
26
27 # @classmethod
28 # def tearDownClass(cls):
29 # """重构类固件"""
30 # print("--tearDownClas")
31
32 #初始化工作
33 def setUp(self):
34 self.a = 3
35 self.b = 1
36 print("--setUp")
37
38 #退出清理工作
39 def tearDown(self):
40 print("--tearDown")
41
42 #具体的测试用例,一定要以test开头
43 def test1sum(self):
44 # res=3/0 抛出异常,执行结果为F
45 #断言两数之和的结果是否是4
46 res=myclass.sum(self.a,self.b)
47 self.assertEqual(res,4,'test sum fail')
48 print('testsum方法')
49
50 def testsub(self):
51 #断言两数之差的结果是否是2
52 self.assertEqual(myclass.sub(self.a,self.b),2,'test sub fail')
53 print('testsub方法')
54
55
56 class aa(unittest.TestCase):
57 # num=2
58 # @unittest.skip('skipping')
59 # @unittest.skipIf(num>1,'num已经大于1')
60 @unittest.skipUnless(sys.platform == "linux", "requires linux")
61 def testaa(self):
62 print('testaa')
63
64
65 if __name__ == '__main__':
66 testCase = unittest.TestLoader().loadTestsFromTestCase(mytest)
67 #将多个测试类加载到测试集合中
68 suite = unittest.TestSuite([testCase])
69 # 创建时间
70 cur_time = time.strftime('%Y-%m-%d_%H_%M_%S')
71 # 文件路径
72 filename = os.path.dirname(os.path.abspath('.'))+'\\report\\'+cur_time+'.html'
73 print("===================================")
74 print("当前文件路径是:",os.path.abspath('.'))
75 print("===================================")
76 print("当前文件路径的上一级文件路径是:",os.path.dirname(os.path.abspath('.')))
77 # 写入一个文件
78 f = open(filename,"wb")
79 # 报告详情
80 runner = HTMLTestRunner.HTMLTestRunner(stream=f,title="测试报告",description='测试报告描述')
81 runner.run(suite)
82
83 # 原始版本
84 # f = open("./report.html","wb")
85 # runner = HTMLTestRunner.HTMLTestRunner(stream=f,title="测试报告",description="测试报告描述")
86 # runner.run(suite)

View Code

Selenium3+python自动化011-unittest生成测试报告(HTMLTestRunner)_html_04

Selenium3+python自动化011-unittest生成测试报告(HTMLTestRunner)_html_05

1 <?xml version="1.0" encoding="UTF-8"?>
2 <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
3 <html xmlns="http://www.w3.org/1999/xhtml">
4 <head>
5 <title>测试报告</title>
6 <meta name="generator" content="HTMLTestRunner 0.8.2"/>
7 <meta http-equiv="Content-Type" content="text/html; charset=UTF-8"/>
8
9 <style type="text/css" media="screen">
10 body { font-family: verdana, arial, helvetica, sans-serif; font-size: 80%; }
11 table { font-size: 100%; }
12 pre { }
13
14 /* -- heading ---------------------------------------------------------------------- */
15 h1 {
16 font-size: 16pt;
17 color: gray;
18 }
19 .heading {
20 margin-top: 0ex;
21 margin-bottom: 1ex;
22 }
23
24 .heading .attribute {
25 margin-top: 1ex;
26 margin-bottom: 0;
27 }
28
29 .heading .description {
30 margin-top: 4ex;
31 margin-bottom: 6ex;
32 }
33
34 /* -- css div popup ------------------------------------------------------------------------ */
35 a.popup_link {
36 }
37
38 a.popup_link:hover {
39 color: red;
40 }
41
42 .popup_window {
43 display: none;
44 position: relative;
45 left: 0px;
46 top: 0px;
47 /*border: solid #627173 1px; */
48 padding: 10px;
49 background-color: #E6E6D6;
50 font-family: "Lucida Console", "Courier New", Courier, monospace;
51 text-align: left;
52 font-size: 8pt;
53 width: 500px;
54 }
55
56 }
57 /* -- report ------------------------------------------------------------------------ */
58 #show_detail_line {
59 margin-top: 3ex;
60 margin-bottom: 1ex;
61 }
62 #result_table {
63 width: 80%;
64 border-collapse: collapse;
65 border: 1px solid #777;
66 }
67 #header_row {
68 font-weight: bold;
69 color: white;
70 background-color: #777;
71 }
72 #result_table td {
73 border: 1px solid #777;
74 padding: 2px;
75 }
76 #total_row { font-weight: bold; }
77 .passClass { background-color: #6c6; }
78 .failClass { background-color: #c60; }
79 .errorClass { background-color: #c00; }
80 .passCase { color: #6c6; }
81 .failCase { color: #c60; font-weight: bold; }
82 .errorCase { color: #c00; font-weight: bold; }
83 .hiddenRow { display: none; }
84 .testcase { margin-left: 2em; }
85
86
87 /* -- ending ---------------------------------------------------------------------- */
88 #ending {
89 }
90
91 </style>
92
93 </head>
94 <body>
95 <script language="javascript" type="text/javascript"><!--
96 output_list = Array();
97
98 /* level - 0:Summary; 1:Failed; 2:All */
99 function showCase(level) {
100 trs = document.getElementsByTagName("tr");
101 for (var i = 0; i < trs.length; i++) {
102 tr = trs[i];
103 id = tr.id;
104 if (id.substr(0,2) == 'ft') {
105 if (level < 1) {
106 tr.className = 'hiddenRow';
107 }
108 else {
109 tr.className = '';
110 }
111 }
112 if (id.substr(0,2) == 'pt') {
113 if (level > 1) {
114 tr.className = '';
115 }
116 else {
117 tr.className = 'hiddenRow';
118 }
119 }
120 }
121 }
122
123
124 function showClassDetail(cid, count) {
125 var id_list = Array(count);
126 var toHide = 1;
127 for (var i = 0; i < count; i++) {
128 tid0 = 't' + cid.substr(1) + '.' + (i+1);
129 tid = 'f' + tid0;
130 tr = document.getElementById(tid);
131 if (!tr) {
132 tid = 'p' + tid0;
133 tr = document.getElementById(tid);
134 }
135 id_list[i] = tid;
136 if (tr.className) {
137 toHide = 0;
138 }
139 }
140 for (var i = 0; i < count; i++) {
141 tid = id_list[i];
142 if (toHide) {
143 document.getElementById('div_'+tid).style.display = 'none'
144 document.getElementById(tid).className = 'hiddenRow';
145 }
146 else {
147 document.getElementById(tid).className = '';
148 }
149 }
150 }
151
152
153 function showTestDetail(div_id){
154 var details_div = document.getElementById(div_id)
155 var displayState = details_div.style.display
156 // alert(displayState)
157 if (displayState != 'block' ) {
158 displayState = 'block'
159 details_div.style.display = 'block'
160 }
161 else {
162 details_div.style.display = 'none'
163 }
164 }
165
166
167 function html_escape(s) {
168 s = s.replace(/&/g,'&');
169 s = s.replace(/</g,'<');
170 s = s.replace(/>/g,'>');
171 return s;
172 }
173
174 /* obsoleted by detail in <div>
175 function showOutput(id, name) {
176 var w = window.open("", //url
177 name,
178 "resizable,scrollbars,status,width=800,height=450");
179 d = w.document;
180 d.write("<pre>");
181 d.write(html_escape(output_list[id]));
182 d.write("\n");
183 d.write("<a href='javascript:window.close()'>close</a>\n");
184 d.write("</pre>\n");
185 d.close();
186 }
187 */
188 --></script>
189
190 <div class='heading'>
191 <h1>测试报告</h1>
192 <p class='attribute'><strong>Start Time:</strong> 2019-07-01 23:11:41</p>
193 <p class='attribute'><strong>Duration:</strong> 0:00:00.001000</p>
194 <p class='attribute'><strong>Status:</strong> Pass 2</p>
195
196 <p class='description'>测试报告描述</p>
197 </div>
198
199
200
201 <p id='show_detail_line'>Show
202 <a href='javascript:showCase(0)'>Summary</a>
203 <a href='javascript:showCase(1)'>Failed</a>
204 <a href='javascript:showCase(2)'>All</a>
205 </p>
206 <table id='result_table'>
207 <colgroup>
208 <col align='left' />
209 <col align='right' />
210 <col align='right' />
211 <col align='right' />
212 <col align='right' />
213 <col align='right' />
214 </colgroup>
215 <tr id='header_row'>
216 <td>Test Group/Test case</td>
217 <td>Count</td>
218 <td>Pass</td>
219 <td>Fail</td>
220 <td>Error</td>
221 <td>View</td>
222 </tr>
223
224 <tr class='passClass'>
225 <td>mytest</td>
226 <td>2</td>
227 <td>2</td>
228 <td>0</td>
229 <td>0</td>
230 <td><a href="javascript:showClassDetail('c1',2)">Detail</a></td>
231 </tr>
232
233 <tr id='pt1.1' class='hiddenRow'>
234 <td class='none'><div class='testcase'>test1sum</div></td>
235 <td colspan='5' align='center'>
236
237 <!--css div popup start-->
238 <a class="popup_link" onfocus='this.blur();' href="javascript:showTestDetail('div_pt1.1')" >
239 pass</a>
240
241 <div id='div_pt1.1' class="popup_window">
242 <div style='text-align: right; color:red;cursor:pointer'>
243 <a onfocus='this.blur();' onclick="document.getElementById('div_pt1.1').style.display = 'none' " >
244 [x]</a>
245 </div>
246 <pre>
247
248 pt1.1:
249
250 </pre>
251 </div>
252 <!--css div popup end-->
253
254 </td>
255 </tr>
256
257 <tr id='pt1.2' class='hiddenRow'>
258 <td class='none'><div class='testcase'>testsub</div></td>
259 <td colspan='5' align='center'>
260
261 <!--css div popup start-->
262 <a class="popup_link" onfocus='this.blur();' href="javascript:showTestDetail('div_pt1.2')" >
263 pass</a>
264
265 <div id='div_pt1.2' class="popup_window">
266 <div style='text-align: right; color:red;cursor:pointer'>
267 <a onfocus='this.blur();' onclick="document.getElementById('div_pt1.2').style.display = 'none' " >
268 [x]</a>
269 </div>
270 <pre>
271
272 pt1.2:
273
274 </pre>
275 </div>
276 <!--css div popup end-->
277
278 </td>
279 </tr>
280
281 <tr id='total_row'>
282 <td>Total</td>
283 <td>2</td>
284 <td>2</td>
285 <td>0</td>
286 <td>0</td>
287 <td> </td>
288 </tr>
289 </table>
290
291 <div id='ending'> </div>
292
293 </body>
294 </html>

测试报告

 

 

Selenium3+python自动化011-unittest生成测试报告(HTMLTestRunner)_html_08