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

一、导入HTMLTestRunner

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

方法2.在python安装文件的Lib目录下新增文件HTMLTestRunner.py

文件内容如下:

  1. """
  2. A TestRunner for use with the Python unit testing framework. It
  3. generates a HTML report to show the result at a glance.
  4.  
  5. The simplest way to use this is to invoke its main method. E.g.
  6.  
  7. import unittest
  8. import HTMLTestRunner
  9.  
  10. ... define your tests ...
  11.  
  12. if __name__ == '__main__':
  13. HTMLTestRunner.main()
  14.  
  15. For more customization options, instantiates a HTMLTestRunner object.
  16. HTMLTestRunner is a counterpart to unittest's TextTestRunner. E.g.
  17.  
  18. # output to a file
  19. fp = file('my_report.html', 'wb')
  20. runner = HTMLTestRunner.HTMLTestRunner(
  21. stream=fp,
  22. title='My unit test',
  23. description='This demonstrates the report output by HTMLTestRunner.'
  24. )
  25.  
  26. # Use an external stylesheet.
  27. # See the Template_mixin class for more customizable options
  28. runner.STYLESHEET_TMPL = '<link rel="stylesheet" href="my_stylesheet.css" type="text/css">'
  29.  
  30. # run the test
  31. runner.run(my_test_suite)
  32.  
  33. ------------------------------------------------------------------------
  34. Copyright (c) 2004-2007, Wai Yip Tung
  35. All rights reserved.
  36.  
  37. Redistribution and use in source and binary forms, with or without
  38. modification, are permitted provided that the following conditions are
  39. met:
  40.  
  41. * Redistributions of source code must retain the above copyright notice,
  42. this list of conditions and the following disclaimer.
  43. * Redistributions in binary form must reproduce the above copyright
  44. notice, this list of conditions and the following disclaimer in the
  45. documentation and/or other materials provided with the distribution.
  46. * Neither the name Wai Yip Tung nor the names of its contributors may be
  47. used to endorse or promote products derived from this software without
  48. specific prior written permission.
  49.  
  50. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS
  51. IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
  52. TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
  53. PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER
  54. OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
  55. EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
  56. PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
  57. PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
  58. LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
  59. NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
  60. SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
  61. """
  62.  
  63. # URL: http://tungwaiyip.info/software/HTMLTestRunner.html
  64.  
  65. __author__ = "Wai Yip Tung"
  66. __version__ = "0.8.2"
  67.  
  68. """
  69. Change History
  70.  
  71. Version 0.8.2
  72. * Show output inline instead of popup window (Viorel Lupu).
  73.  
  74. Version in 0.8.1
  75. * Validated XHTML (Wolfgang Borgert).
  76. * Added description of test classes and test cases.
  77.  
  78. Version in 0.8.0
  79. * Define Template_mixin class for customization.
  80. * Workaround a IE 6 bug that it does not treat <script> block as CDATA.
  81.  
  82. Version in 0.7.1
  83. * Back port to Python 2.3 (Frank Horowitz).
  84. * Fix missing scroll bars in detail log (Podi).
  85. """
  86.  
  87. # TODO: color stderr
  88. # TODO: simplify javascript using ,ore than 1 class in the class attribute?
  89.  
  90. import datetime
  91. import StringIO
  92. import sys
  93. import time
  94. import unittest
  95. from xml.sax import saxutils
  96.  
  97. # ------------------------------------------------------------------------
  98. # The redirectors below are used to capture output during testing. Output
  99. # sent to sys.stdout and sys.stderr are automatically captured. However
  100. # in some cases sys.stdout is already cached before HTMLTestRunner is
  101. # invoked (e.g. calling logging.basicConfig). In order to capture those
  102. # output, use the redirectors for the cached stream.
  103. #
  104. # e.g.
  105. # >>> logging.basicConfig(stream=HTMLTestRunner.stdout_redirector)
  106. # >>>
  107.  
  108. class OutputRedirector(object):
  109. """ Wrapper to redirect stdout or stderr """
  110. def __init__(self, fp):
  111. self.fp = fp
  112.  
  113. def write(self, s):
  114. self.fp.write(s)
  115.  
  116. def writelines(self, lines):
  117. self.fp.writelines(lines)
  118.  
  119. def flush(self):
  120. self.fp.flush()
  121.  
  122. stdout_redirector = OutputRedirector(sys.stdout)
  123. stderr_redirector = OutputRedirector(sys.stderr)
  124.  
  125. # ----------------------------------------------------------------------
  126. # Template
  127.  
  128. class Template_mixin(object):
  129. """
  130. Define a HTML template for report customerization and generation.
  131.  
  132. Overall structure of an HTML report
  133.  
  134. HTML
  135. +------------------------+
  136. |<html> |
  137. | <head> |
  138. | |
  139. | STYLESHEET |
  140. | +----------------+ |
  141. | | | |
  142. | +----------------+ |
  143. | |
  144. | </head> |
  145. | |
  146. | <body> |
  147. | |
  148. | HEADING |
  149. | +----------------+ |
  150. | | | |
  151. | +----------------+ |
  152. | |
  153. | REPORT |
  154. | +----------------+ |
  155. | | | |
  156. | +----------------+ |
  157. | |
  158. | ENDING |
  159. | +----------------+ |
  160. | | | |
  161. | +----------------+ |
  162. | |
  163. | </body> |
  164. |</html> |
  165. +------------------------+
  166. """
  167.  
  168. STATUS = {
  169. 0: 'pass',
  170. 1: 'fail',
  171. 2: 'error',
  172. }
  173.  
  174. DEFAULT_TITLE = 'Unit Test Report'
  175. DEFAULT_DESCRIPTION = ''
  176.  
  177. # ------------------------------------------------------------------------
  178. # HTML Template
  179.  
  180. HTML_TMPL = r"""<?xml version="1.0" encoding="UTF-8"?>
  181. <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
  182. <html xmlns="http://www.w3.org/1999/xhtml">
  183. <head>
  184. <title>%(title)s</title>
  185. <meta name="generator" content="%(generator)s"/>
  186. <meta http-equiv="Content-Type" content="text/html; charset=UTF-8"/>
  187. %(stylesheet)s
  188. </head>
  189. <body>
  190. <script language="javascript" type="text/javascript"><!--
  191. output_list = Array();
  192.  
  193. /* level - 0:Summary; 1:Failed; 2:All */
  194. function showCase(level) {
  195. trs = document.getElementsByTagName("tr");
  196. for (var i = 0; i < trs.length; i++) {
  197. tr = trs[i];
  198. id = tr.id;
  199. if (id.substr(0,2) == 'ft') {
  200. if (level < 1) {
  201. tr.className = 'hiddenRow';
  202. }
  203. else {
  204. tr.className = '';
  205. }
  206. }
  207. if (id.substr(0,2) == 'pt') {
  208. if (level > 1) {
  209. tr.className = '';
  210. }
  211. else {
  212. tr.className = 'hiddenRow';
  213. }
  214. }
  215. }
  216. }
  217.  
  218. function showClassDetail(cid, count) {
  219. var id_list = Array(count);
  220. var toHide = 1;
  221. for (var i = 0; i < count; i++) {
  222. tid0 = 't' + cid.substr(1) + '.' + (i+1);
  223. tid = 'f' + tid0;
  224. tr = document.getElementById(tid);
  225. if (!tr) {
  226. tid = 'p' + tid0;
  227. tr = document.getElementById(tid);
  228. }
  229. id_list[i] = tid;
  230. if (tr.className) {
  231. toHide = 0;
  232. }
  233. }
  234. for (var i = 0; i < count; i++) {
  235. tid = id_list[i];
  236. if (toHide) {
  237. document.getElementById('div_'+tid).style.display = 'none'
  238. document.getElementById(tid).className = 'hiddenRow';
  239. }
  240. else {
  241. document.getElementById(tid).className = '';
  242. }
  243. }
  244. }
  245.  
  246. function showTestDetail(div_id){
  247. var details_div = document.getElementById(div_id)
  248. var displayState = details_div.style.display
  249. // alert(displayState)
  250. if (displayState != 'block' ) {
  251. displayState = 'block'
  252. details_div.style.display = 'block'
  253. }
  254. else {
  255. details_div.style.display = 'none'
  256. }
  257. }
  258.  
  259. function html_escape(s) {
  260. s = s.replace(/&/g,'&amp;');
  261. s = s.replace(/</g,'&lt;');
  262. s = s.replace(/>/g,'&gt;');
  263. return s;
  264. }
  265.  
  266. /* obsoleted by detail in <div>
  267. function showOutput(id, name) {
  268. var w = window.open("", //url
  269. name,
  270. "resizable,scrollbars,status,width=800,height=450");
  271. d = w.document;
  272. d.write("<pre>");
  273. d.write(html_escape(output_list[id]));
  274. d.write("\n");
  275. d.write("<a href='javascript:window.close()'>close</a>\n");
  276. d.write("</pre>\n");
  277. d.close();
  278. }
  279. */
  280. --></script>
  281.  
  282. %(heading)s
  283. %(report)s
  284. %(ending)s
  285.  
  286. </body>
  287. </html>
  288. """
  289. # variables: (title, generator, stylesheet, heading, report, ending)
  290.  
  291. # ------------------------------------------------------------------------
  292. # Stylesheet
  293. #
  294. # alternatively use a <link> for external style sheet, e.g.
  295. # <link rel="stylesheet" href="$url" type="text/css">
  296.  
  297. STYLESHEET_TMPL = """
  298. <style type="text/css" media="screen">
  299. body { font-family: verdana, arial, helvetica, sans-serif; font-size: 80%; }
  300. table { font-size: 100%; }
  301. pre { }
  302.  
  303. /* -- heading ---------------------------------------------------------------------- */
  304. h1 {
  305. font-size: 16pt;
  306. color: gray;
  307. }
  308. .heading {
  309. margin-top: 0ex;
  310. margin-bottom: 1ex;
  311. }
  312.  
  313. .heading .attribute {
  314. margin-top: 1ex;
  315. margin-bottom: 0;
  316. }
  317.  
  318. .heading .description {
  319. margin-top: 4ex;
  320. margin-bottom: 6ex;
  321. }
  322.  
  323. /* -- css div popup ------------------------------------------------------------------------ */
  324. a.popup_link {
  325. }
  326.  
  327. a.popup_link:hover {
  328. color: red;
  329. }
  330.  
  331. .popup_window {
  332. display: none;
  333. position: relative;
  334. left: 0px;
  335. top: 0px;
  336. /*border: solid #627173 1px; */
  337. padding: 10px;
  338. background-color: #E6E6D6;
  339. font-family: "Lucida Console", "Courier New", Courier, monospace;
  340. text-align: left;
  341. font-size: 8pt;
  342. width: 500px;
  343. }
  344.  
  345. }
  346. /* -- report ------------------------------------------------------------------------ */
  347. #show_detail_line {
  348. margin-top: 3ex;
  349. margin-bottom: 1ex;
  350. }
  351. #result_table {
  352. width: 80%;
  353. border-collapse: collapse;
  354. border: 1px solid #777;
  355. }
  356. #header_row {
  357. font-weight: bold;
  358. color: white;
  359. background-color: #777;
  360. }
  361. #result_table td {
  362. border: 1px solid #777;
  363. padding: 2px;
  364. }
  365. #total_row { font-weight: bold; }
  366. .passClass { background-color: #6c6; }
  367. .failClass { background-color: #c60; }
  368. .errorClass { background-color: #c00; }
  369. .passCase { color: #6c6; }
  370. .failCase { color: #c60; font-weight: bold; }
  371. .errorCase { color: #c00; font-weight: bold; }
  372. .hiddenRow { display: none; }
  373. .testcase { margin-left: 2em; }
  374.  
  375. /* -- ending ---------------------------------------------------------------------- */
  376. #ending {
  377. }
  378.  
  379. </style>
  380. """
  381.  
  382. # ------------------------------------------------------------------------
  383. # Heading
  384. #
  385.  
  386. HEADING_TMPL = """<div class='heading'>
  387. <h1>%(title)s</h1>
  388. %(parameters)s
  389. <p class='description'>%(description)s</p>
  390. </div>
  391.  
  392. """ # variables: (title, parameters, description)
  393.  
  394. HEADING_ATTRIBUTE_TMPL = """<p class='attribute'><strong>%(name)s:</strong> %(value)s</p>
  395. """ # variables: (name, value)
  396.  
  397. # ------------------------------------------------------------------------
  398. # Report
  399. #
  400.  
  401. REPORT_TMPL = """
  402. <p id='show_detail_line'>Show
  403. <a href='javascript:showCase(0)'>Summary</a>
  404. <a href='javascript:showCase(1)'>Failed</a>
  405. <a href='javascript:showCase(2)'>All</a>
  406. </p>
  407. <table id='result_table'>
  408. <colgroup>
  409. <col align='left' />
  410. <col align='right' />
  411. <col align='right' />
  412. <col align='right' />
  413. <col align='right' />
  414. <col align='right' />
  415. </colgroup>
  416. <tr id='header_row'>
  417. <td>Test Group/Test case</td>
  418. <td>Count</td>
  419. <td>Pass</td>
  420. <td>Fail</td>
  421. <td>Error</td>
  422. <td>View</td>
  423. </tr>
  424. %(test_list)s
  425. <tr id='total_row'>
  426. <td>Total</td>
  427. <td>%(count)s</td>
  428. <td>%(Pass)s</td>
  429. <td>%(fail)s</td>
  430. <td>%(error)s</td>
  431. <td>&nbsp;</td>
  432. </tr>
  433. </table>
  434. """ # variables: (test_list, count, Pass, fail, error)
  435.  
  436. REPORT_CLASS_TMPL = r"""
  437. <tr class='%(style)s'>
  438. <td>%(desc)s</td>
  439. <td>%(count)s</td>
  440. <td>%(Pass)s</td>
  441. <td>%(fail)s</td>
  442. <td>%(error)s</td>
  443. <td><a href="javascript:showClassDetail('%(cid)s',%(count)s)">Detail</a></td>
  444. </tr>
  445. """ # variables: (style, desc, count, Pass, fail, error, cid)
  446.  
  447. REPORT_TEST_WITH_OUTPUT_TMPL = r"""
  448. <tr id='%(tid)s' class='%(Class)s'>
  449. <td class='%(style)s'><div class='testcase'>%(desc)s</div></td>
  450. <td colspan='5' align='center'>
  451.  
  452. <!--css div popup start-->
  453. <a class="popup_link" onfocus='this.blur();' href="javascript:showTestDetail('div_%(tid)s')" >
  454. %(status)s</a>
  455.  
  456. <div id='div_%(tid)s' class="popup_window">
  457. <div style='text-align: right; color:red;cursor:pointer'>
  458. <a onfocus='this.blur();' onclick="document.getElementById('div_%(tid)s').style.display = 'none' " >
  459. [x]</a>
  460. </div>
  461. <pre>
  462. %(script)s
  463. </pre>
  464. </div>
  465. <!--css div popup end-->
  466.  
  467. </td>
  468. </tr>
  469. """ # variables: (tid, Class, style, desc, status)
  470.  
  471. REPORT_TEST_NO_OUTPUT_TMPL = r"""
  472. <tr id='%(tid)s' class='%(Class)s'>
  473. <td class='%(style)s'><div class='testcase'>%(desc)s</div></td>
  474. <td colspan='5' align='center'>%(status)s</td>
  475. </tr>
  476. """ # variables: (tid, Class, style, desc, status)
  477.  
  478. REPORT_TEST_OUTPUT_TMPL = r"""
  479. %(id)s: %(output)s
  480. """ # variables: (id, output)
  481.  
  482. # ------------------------------------------------------------------------
  483. # ENDING
  484. #
  485.  
  486. ENDING_TMPL = """<div id='ending'>&nbsp;</div>"""
  487.  
  488. # -------------------- The end of the Template class -------------------
  489.  
  490. TestResult = unittest.TestResult
  491.  
  492. class _TestResult(TestResult):
  493. # note: _TestResult is a pure representation of results.
  494. # It lacks the output and reporting ability compares to unittest._TextTestResult.
  495.  
  496. def __init__(self, verbosity=1):
  497. TestResult.__init__(self)
  498. self.stdout0 = None
  499. self.stderr0 = None
  500. self.success_count = 0
  501. self.failure_count = 0
  502. self.error_count = 0
  503. self.verbosity = verbosity
  504.  
  505. # result is a list of result in 4 tuple
  506. # (
  507. # result code (0: success; 1: fail; 2: error),
  508. # TestCase object,
  509. # Test output (byte string),
  510. # stack trace,
  511. # )
  512. self.result = []
  513.  
  514. def startTest(self, test):
  515. TestResult.startTest(self, test)
  516. # just one buffer for both stdout and stderr
  517. self.outputBuffer = StringIO.StringIO()
  518. stdout_redirector.fp = self.outputBuffer
  519. stderr_redirector.fp = self.outputBuffer
  520. self.stdout0 = sys.stdout
  521. self.stderr0 = sys.stderr
  522. sys.stdout = stdout_redirector
  523. sys.stderr = stderr_redirector
  524.  
  525. def complete_output(self):
  526. """
  527. Disconnect output redirection and return buffer.
  528. Safe to call multiple times.
  529. """
  530. if self.stdout0:
  531. sys.stdout = self.stdout0
  532. sys.stderr = self.stderr0
  533. self.stdout0 = None
  534. self.stderr0 = None
  535. return self.outputBuffer.getvalue()
  536.  
  537. def stopTest(self, test):
  538. # Usually one of addSuccess, addError or addFailure would have been called.
  539. # But there are some path in unittest that would bypass this.
  540. # We must disconnect stdout in stopTest(), which is guaranteed to be called.
  541. self.complete_output()
  542.  
  543. def addSuccess(self, test):
  544. self.success_count += 1
  545. TestResult.addSuccess(self, test)
  546. output = self.complete_output()
  547. self.result.append((0, test, output, ''))
  548. if self.verbosity > 1:
  549. sys.stderr.write('ok ')
  550. sys.stderr.write(str(test))
  551. sys.stderr.write('\n')
  552. else:
  553. sys.stderr.write('.')
  554.  
  555. def addError(self, test, err):
  556. self.error_count += 1
  557. TestResult.addError(self, test, err)
  558. _, _exc_str = self.errors[-1]
  559. output = self.complete_output()
  560. self.result.append((2, test, output, _exc_str))
  561. if self.verbosity > 1:
  562. sys.stderr.write('E ')
  563. sys.stderr.write(str(test))
  564. sys.stderr.write('\n')
  565. else:
  566. sys.stderr.write('E')
  567.  
  568. def addFailure(self, test, err):
  569. self.failure_count += 1
  570. TestResult.addFailure(self, test, err)
  571. _, _exc_str = self.failures[-1]
  572. output = self.complete_output()
  573. self.result.append((1, test, output, _exc_str))
  574. if self.verbosity > 1:
  575. sys.stderr.write('F ')
  576. sys.stderr.write(str(test))
  577. sys.stderr.write('\n')
  578. else:
  579. sys.stderr.write('F')
  580.  
  581. class HTMLTestRunner(Template_mixin):
  582. """
  583. """
  584. def __init__(self, stream=sys.stdout, verbosity=1, title=None, description=None):
  585. self.stream = stream
  586. self.verbosity = verbosity
  587. if title is None:
  588. self.title = self.DEFAULT_TITLE
  589. else:
  590. self.title = title
  591. if description is None:
  592. self.description = self.DEFAULT_DESCRIPTION
  593. else:
  594. self.description = description
  595.  
  596. self.startTime = datetime.datetime.now()
  597.  
  598. def run(self, test):
  599. "Run the given test case or test suite."
  600. result = _TestResult(self.verbosity)
  601. test(result)
  602. self.stopTime = datetime.datetime.now()
  603. self.generateReport(test, result)
  604. print >>sys.stderr, '\nTime Elapsed: %s' % (self.stopTime-self.startTime)
  605. return result
  606.  
  607. def sortResult(self, result_list):
  608. # unittest does not seems to run in any particular order.
  609. # Here at least we want to group them together by class.
  610. rmap = {}
  611. classes = []
  612. for n,t,o,e in result_list:
  613. cls = t.__class__
  614. if not rmap.has_key(cls):
  615. rmap[cls] = []
  616. classes.append(cls)
  617. rmap[cls].append((n,t,o,e))
  618. r = [(cls, rmap[cls]) for cls in classes]
  619. return r
  620.  
  621. def getReportAttributes(self, result):
  622. """
  623. Return report attributes as a list of (name, value).
  624. Override this to add custom attributes.
  625. """
  626. startTime = str(self.startTime)[:19]
  627. duration = str(self.stopTime - self.startTime)
  628. status = []
  629. if result.success_count: status.append('Pass %s' % result.success_count)
  630. if result.failure_count: status.append('Failure %s' % result.failure_count)
  631. if result.error_count: status.append('Error %s' % result.error_count )
  632. if status:
  633. status = ' '.join(status)
  634. else:
  635. status = 'none'
  636. return [
  637. ('Start Time', startTime),
  638. ('Duration', duration),
  639. ('Status', status),
  640. ]
  641.  
  642. def generateReport(self, test, result):
  643. report_attrs = self.getReportAttributes(result)
  644. generator = 'HTMLTestRunner %s' % __version__
  645. stylesheet = self._generate_stylesheet()
  646. heading = self._generate_heading(report_attrs)
  647. report = self._generate_report(result)
  648. ending = self._generate_ending()
  649. output = self.HTML_TMPL % dict(
  650. title = saxutils.escape(self.title),
  651. generator = generator,
  652. stylesheet = stylesheet,
  653. heading = heading,
  654. report = report,
  655. ending = ending,
  656. )
  657. self.stream.write(output.encode('utf8'))
  658.  
  659. def _generate_stylesheet(self):
  660. return self.STYLESHEET_TMPL
  661.  
  662. def _generate_heading(self, report_attrs):
  663. a_lines = []
  664. for name, value in report_attrs:
  665. line = self.HEADING_ATTRIBUTE_TMPL % dict(
  666. name = saxutils.escape(name),
  667. value = saxutils.escape(value),
  668. )
  669. a_lines.append(line)
  670. heading = self.HEADING_TMPL % dict(
  671. title = saxutils.escape(self.title),
  672. parameters = ''.join(a_lines),
  673. description = saxutils.escape(self.description),
  674. )
  675. return heading
  676.  
  677. def _generate_report(self, result):
  678. rows = []
  679. sortedResult = self.sortResult(result.result)
  680. for cid, (cls, cls_results) in enumerate(sortedResult):
  681. # subtotal for a class
  682. np = nf = ne = 0
  683. for n,t,o,e in cls_results:
  684. if n == 0: np += 1
  685. elif n == 1: nf += 1
  686. else: ne += 1
  687.  
  688. # format class description
  689. if cls.__module__ == "__main__":
  690. name = cls.__name__
  691. else:
  692. name = "%s.%s" % (cls.__module__, cls.__name__)
  693. doc = cls.__doc__ and cls.__doc__.split("\n")[0] or ""
  694. desc = doc and '%s: %s' % (name, doc) or name
  695.  
  696. row = self.REPORT_CLASS_TMPL % dict(
  697. style = ne > 0 and 'errorClass' or nf > 0 and 'failClass' or 'passClass',
  698. desc = desc,
  699. count = np+nf+ne,
  700. Pass = np,
  701. fail = nf,
  702. error = ne,
  703. cid = 'c%s' % (cid+1),
  704. )
  705. rows.append(row)
  706.  
  707. for tid, (n,t,o,e) in enumerate(cls_results):
  708. self._generate_report_test(rows, cid, tid, n, t, o, e)
  709.  
  710. report = self.REPORT_TMPL % dict(
  711. test_list = ''.join(rows),
  712. count = str(result.success_count+result.failure_count+result.error_count),
  713. Pass = str(result.success_count),
  714. fail = str(result.failure_count),
  715. error = str(result.error_count),
  716. )
  717. return report
  718.  
  719. def _generate_report_test(self, rows, cid, tid, n, t, o, e):
  720. # e.g. 'pt1.1', 'ft1.1', etc
  721. has_output = bool(o or e)
  722. tid = (n == 0 and 'p' or 'f') + 't%s.%s' % (cid+1,tid+1)
  723. name = t.id().split('.')[-1]
  724. doc = t.shortDescription() or ""
  725. desc = doc and ('%s: %s' % (name, doc)) or name
  726. tmpl = has_output and self.REPORT_TEST_WITH_OUTPUT_TMPL or self.REPORT_TEST_NO_OUTPUT_TMPL
  727.  
  728. # o and e should be byte string because they are collected from stdout and stderr?
  729. if isinstance(o,str):
  730. # TODO: some problem with 'string_escape': it escape \n and mess up formating
  731. # uo = unicode(o.encode('string_escape'))
  732. uo = o.decode('latin-1')
  733. else:
  734. uo = o
  735. if isinstance(e,str):
  736. # TODO: some problem with 'string_escape': it escape \n and mess up formating
  737. # ue = unicode(e.encode('string_escape'))
  738. ue = e.decode('latin-1')
  739. else:
  740. ue = e
  741.  
  742. script = self.REPORT_TEST_OUTPUT_TMPL % dict(
  743. id = tid,
  744. output = saxutils.escape(uo+ue),
  745. )
  746.  
  747. row = tmpl % dict(
  748. tid = tid,
  749. Class = (n == 0 and 'hiddenRow' or 'none'),
  750. style = n == 2 and 'errorCase' or (n == 1 and 'failCase' or 'none'),
  751. desc = desc,
  752. script = script,
  753. status = self.STATUS[n],
  754. )
  755. rows.append(row)
  756. if not has_output:
  757. return
  758.  
  759. def _generate_ending(self):
  760. return self.ENDING_TMPL
  761.  
  762. ##############################################################################
  763. # Facilities for running tests from the command line
  764. ##############################################################################
  765.  
  766. # Note: Reuse unittest.TestProgram to launch test. In the future we may
  767. # build our own launcher to support more specific command line
  768. # parameters like test title, CSS, etc.
  769. class TestProgram(unittest.TestProgram):
  770. """
  771. A variation of the unittest.TestProgram. Please refer to the base
  772. class for command line parameters.
  773. """
  774. def runTests(self):
  775. # Pick HTMLTestRunner as the default test runner.
  776. # base class's testRunner parameter is not useful because it means
  777. # we have to instantiate HTMLTestRunner before we know self.verbosity.
  778. if self.testRunner is None:
  779. self.testRunner = HTMLTestRunner(verbosity=self.verbosity)
  780. unittest.TestProgram.runTests(self)
  781.  
  782. main = TestProgram
  783.  
  784. ##############################################################################
  785. # Executing this module from the command line
  786. ##############################################################################
  787.  
  788. if __name__ == "__main__":
  789. main(module=None)

unittest中HTMLTestRunner模块生成的更多相关文章

  1. Python中random模块生成随机数详解

    Python中random模块生成随机数详解 本文给大家汇总了一下在Python中random模块中最常用的生成随机数的方法,有需要的小伙伴可以参考下 Python中的random模块用于生成随机数. ...

  2. Python 中 使用 HTMLTestRunner 模块生成测试报告

     使用 HTMLTestRunner 模块可以生成测试报告,但是系统自带的报告不详细,不好看,所以找了一份详细的报告 HTMLTestRunner 模板,直接导入就能使用 两种方法生成HTML报告,都 ...

  3. 使用 HTMLTestRunner 模块生成HTML格式的测试报告文件

    1.下载HTMLTestRunner.py HTMLTestRunner 是 Python 标准库的 unittest 模块的一个扩展.它生成易于使用的 HTML 测试报告.HTMLTestRunne ...

  4. 使用HTMLTestRunner模块生成测试报告

    步骤: 1.下载HTMLTestRunner模块 HTMLTestRunnerCN.py是中文版本的,EN是英文版本的,将要使用的版本放到Python安装目录下lib文件夹中,然后试试看能不能impo ...

  5. Python单元测试unittest与HTMLTestRunner报告生成

    本文为简单介绍,使用python自带模块unittest来进行单元测试 首先我们有一个需要测试的类,employee.py  定义了涨薪的方法.我们需要测试这个类的功能是否正确. class Empl ...

  6. python模块学习之HTMLTestRunner模块生成HTML测试报告

    #!/usr/bin/env python #-*- coding:utf-8 -*- from HTMLTestRunner import HTMLTestRunner import time im ...

  7. 解惑unittest框架中导入HTMLTestRunner模块后正常运行却无法生成HTML报告问题

    1.HTMLTestRunner介绍 HTMLTestRunner是一个第三方的unittest HTML报告库,用于python单元测试框架的TestRunner.它是生成一个HTML报告,以一目了 ...

  8. 基于python语言的自动化测试中生成html的测试报告时HtmlTestRunner模块常见问题

    一.导入了HTMLTestRunner模块,报错:No module named StringIO,在python3.x中确实没有,在第94行引入的名称改成import io,539行要改成self. ...

  9. unittest(生成 HTMLTestRunner 模块)

    一:生成 HTMLTestRunner 模块 unittest 里面是不能生成 html 格式报告的,需要导入一个第三方的模块:HTMLTestRunner 方法1.这个模块下载不能通过 pip 安装 ...

随机推荐

  1. Net基础篇_学习笔记_第九天_数组_三个练习

    练习一: using System; using System.Collections.Generic; using System.Linq; using System.Text; using Sys ...

  2. rpm简单使用

    rpm描述:利用源码包编译成rpm时,会去指定安装好这个包的位置本质:解压,然后拷贝到相关的目录,然后执行脚本 vstpd-3.0.2-9.el7.x86_64.rpm 包名 版本 release 架 ...

  3. Hive数据导入/导出

    1.1 导入/导出规则 EXPORT 命令导出数据表或分区,与元数据一起输出到指定位置.又可以从这个输出位置移动到不同的Hadoop 或Hive 实例中,并且使用IMPORT 命令导入. 当导出一个分 ...

  4. springboot之全局处理异常封装

    springboot之全局处理异常封装 简介 在项目中经常出现系统异常的情况,比如NullPointerException等等.如果默认未处理的情况下,springboot会响应默认的错误提示,这样对 ...

  5. Windows下更换MAC地址

    使用TMAC软件是最佳方案.官网地址:www.technitium.com

  6. 使用Consul做leader选举的方案

    在分布式集群部署模式下,为了维护数据一致性,通常需要选举出一个leader来进行协调,并且在leader挂掉后能从集群中选举出一个新的leader.选举leader的方案有很多种,对Paxos和Raf ...

  7. 简单粗暴的关键两部实现连接远程云服务器数据库SqlServer 2012

    要连上远程服务器的数据库,前面的那些数据库配置就不说了,网上都一样. 下面讲讲关键的两点,也是我尝试普通的方法无效后通过下面的方法成功连上的. 1.点开云服务器的安全组,看看里面的端口是否都放行了.我 ...

  8. java数据结构——递归(Recursion)例题持续更新中

    继续学习数据结构递归,什么是递归呢?字面理解就是先递出去,然后回归,递归核心思想就是直接或间接调用本身,好比从前有座山,山里有位老和尚,在给小和尚讲故事,讲的是从前有座山,山里有位老和尚,在给小和尚讲 ...

  9. calico的ipip与bgp的模式分析

    1.前言 BGP工作模式: bgp工作模式和flannel的host-gw模式几乎一样: bird是bgd的客户端,与集群中其它节点的bird进行通信,以便于交换各自的路由信息: 随着节点数量N的增加 ...

  10. 3.form表单

    1.Form标签:用来将表单外的内容与表单进行关联.其主要元素有input,button,select. action属性:指定表单的发送地址. Novalidate属性:数据提交时不校验. Targ ...