Python+Requests+Unittest+Excel+HtmltestRunner生成自动化测试框架

流程

1.接口文档

2.读取接口文档

3.封装request的类

4.unittest类 testcase 就是读取文档类中的用例

5.htmltestrunner

1.接口文档

从开发获取接口文档整理编写测试用例(url,method,paramter,code,response)

2.读取接口文档

读取接口文档得到我们要发起请求的接口参数

3.封装request的类

封装request类,主要作用对浏览器发起请求(请求参数Excel表中获取)。assert断言预期结果是否达到

实际结果标准。

4.unittest类 testcase 就是读取文档类中的用例

unittest测试用例类,用于存放我们的测试用例。unittest.TestCase作用用例断言

5.htmltestrunner

实例化suit组,里面存放我们要测的用例。结合htmltestrunner生成测试用例报告


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

Read_excel.py---->用于读取Excel表中数据

  1. '''
  2. 安装 pip install xlrd
  3. 打开文件 x1 = xlrd.open_workbook("data.xlsx")
  4. 获取sheet对象
  5. print 'sheet_names:', x1.sheet_names() # 获取所有sheet名字
  6. print 'sheet_number:', x1.nsheets # 获取sheet数量
  7. print 'sheet_object:', x1.sheets() # 获取所有sheet对象
  8. print 'By_name:', x1.sheet_by_name("test") # 通过sheet名查找
  9. print 'By_index:', x1.sheet_by_index(3) # 通过索引查找
  10. '''
  11. '''
  12. 从excel文件中获取接口数据
  13. 1.打开文件
  14. 2.通过sheet名查找定位 一般在Excel表最下面
  15. 3.取第一行作为key值
  16. 4.获取总行数
  17. 5.获取总列数
  18. 6.通过方法循环字典获取列表中的值(每行的data) ---->从excel里面读取测试数据,返回字典格式
  19. '''
  20. import xlrd
  21. class ExcelUtil():
  22. def __init__(self, excelPath, sheetName="Sheet1"):
  23. #打开文件
  24. self.data = xlrd.open_workbook(excelPath)
  25. #通过sheet名查找
  26. self.table = self.data.sheet_by_name(sheetName)
  27. # 获取第一行作为key值
  28. self.keys = self.table.row_values(0)
  29. # 获取总行数
  30. self.rowNum = self.table.nrows
  31. # 获取总列数
  32. self.colNum = self.table.ncols
  33. '''
  34. 1.首先判断excel表中总行数是否大于1,excel表中第一行一般都是类似于标题取第一行作为key,其余一般是内容
  35. '''
  36. def dict_data(self):
  37. if self.rowNum <= 1:
  38. print("总行数小于1")
  39. else:
  40. r = []
  41. j = 1
  42. for i in list(range(self.rowNum-1)):
  43. s = {}
  44. # 从第二行取对应values值
  45. s['rowNum'] = i+2
  46. values = self.table.row_values(j)
  47. for x in list(range(self.colNum)):
  48. s[self.keys[x]] = values[x]
  49. r.append(s)
  50. j += 1
  51. return r
  52. if __name__ == "__main__":
  53. filepath = "./Excel_Data/jiekou.xls"
  54. sheetName = "Sheet1"
  55. data = ExcelUtil(filepath, sheetName)
  56. print(data.dict_data())

ReqstCase.py----->封装requests请求类接收请求参数发起请求

  1. """
  2. 封装request请求方法
  3. 1.把从excel读取处理的数据作为请求参数,封装request请求方法,传入请求参数,并返回结果
  4. 2.为了不污染测试的数据,出报告的时候先将测试的excel复制到新的excel xlwd
  5. 3.把测试返回的结果,在新的Excel里面写入数据
  6. """
  7. #requests请求库,对浏览器发起请求
  8. import requests
  9. #python自带单元测试模块
  10. import unittest
  11. #导包 excel中处理的数据
  12. from Read_excel import ExcelUtil
  13. #定义发送请求类
  14. class SendReq():
  15. def sedrq(self,data):
  16. #获取excel中请求方法
  17. '''
  18. 因为获取到的值是列表套字典,所以我们要循环依此遍历,得到具体行的具体参数
  19. :param data:
  20. :return:
  21. '''
  22. codelist = []
  23. for da in data:
  24. method = da['method']
  25. # 获取excel中请求地址
  26. url = da['url']
  27. #获取excel中请求数据
  28. paramter = eval(da['parameter']) #eval()执行字符串表达式,并返回表达式的值
  29. #requests.request里面参数 method url 参数(请求方式,请求地址,请求参数)
  30. # params-->接收请求参数
  31. rq = requests.request(method,url,params=paramter)
  32. codelist.append(rq.status_code)
  33. # print(rq.status_code)
  34. # print(codelist)
  35. return codelist
  36. if __name__ == '__main__':
  37. '''接收Excel中处理数据'''
  38. re = ExcelUtil('./Excel_Data/jiekou.xls')
  39. '''接收请求参数'''
  40. data = re.dict_data()
  41. '''定义请求类'''
  42. srq = SendReq()
  43. srq.sedrq(data)
  44. code = srq.sedrq(data)
  45. for c in code:
  46. print(c)

test_mathfunc.py---->用例断言(我们要测试的用例方法)

  1. '''
  2. 1.导入unittest模块
  3. 2.导包我们来判断断言以及requests发起请求的响应结果是否一致
  4. '''
  5. import xlrd
  6. #Python自带unittest单元测试报告
  7. import unittest
  8. #用来接收从excel中读取出来的数据
  9. from Read_excel import ExcelUtil
  10. #用来接收request请求发送的请求响应信息
  11. from ReqstCase import SendReq
  12. class TestMathFunc(unittest.TestCase):
  13. def setUp(self) -> None:
  14. print('开始')
  15. def test_1(self):
  16. '''第一个接口测试用例'''
  17. rex = ExcelUtil('./Excel_Data/jiekou.xls')
  18. data = rex.dict_data()
  19. #第一个接口参数值
  20. da = data[0]
  21. code = da['yuqi_code']
  22. # print(da)
  23. srq = SendReq()
  24. rq_code = srq.sedrq(data)[0]
  25. # res = srq.sedrq(da)
  26. # print(res)
  27. assert rq_code == code
  28. def test_2(self):
  29. '''第二个接口测试用例'''
  30. rex = ExcelUtil('./Excel_Data/jiekou.xls')
  31. data = rex.dict_data()
  32. # 第二个接口参数值
  33. da = data[1]
  34. code = da['yuqi_code']
  35. # print(da)
  36. srq = SendReq()
  37. rq_code = srq.sedrq(data)[1]
  38. # res = srq.sedrq(da)
  39. # print(res)
  40. assert rq_code == code
  41. def tearDown(self) -> None:
  42. print('结束')
  43. if __name__ == '__main__':
  44. unittest.main()

suitcase.py---->生成测试报告

  1. import unittest
  2. from test_mathfunc import TestMathFunc
  3. from Common.HTMLTestRunner import HTMLTestRunner
  4. class Test():
  5. # tp = Tptest()
  6. def tps(self):
  7. suit = unittest.TestSuite()
  8. test = [TestMathFunc('test_1'),TestMathFunc('test_2')]
  9. suit.addTests(test)
  10. with open("./inter.html", "wb") as f:
  11. HTMLTestRunner(
  12. stream=f,
  13. title="接口测试",
  14. description="python + unittest + excel + requests + Htmltestrunner",
  15. verbosity=2
  16. ).run(suit)
  17. #python+requests+unittest+htmltestrunner+ecl 接口自动化的测试框架
  18. t = Test()
  19. t.tps()

jiekou.xls ----->接口参数请求数据来源

inter.html ------->测试报告用例样式

python+requests+unittest+htmltestrunner+Excel生成接口自动化的测试框架的更多相关文章

  1. 基于Python+Requests+Pytest+YAML+Allure实现接口自动化

    本项目实现接口自动化的技术选型:Python+Requests+Pytest+YAML+Allure ,主要是针对之前开发的一个接口项目来进行学习,通过 Python+Requests 来发送和处理H ...

  2. Python+Selenium----使用HTMLTestRunner.py生成自动化测试报告2(使用PyCharm )

    1.说明 在我前一篇文件(Python+Selenium----使用HTMLTestRunner.py生成自动化测试报告1(使用IDLE ))中简单的写明了,如何生产测试报告,但是使用IDLE很麻烦, ...

  3. 某互联网后台自动化组合测试框架RF+Sikuli+Python脚本

    某互联网后台自动化组合测试框架RF+Sikuli+Python脚本 http://www.jianshu.com/p/b3e204c8651a 字数949 阅读323 评论1 喜欢0 一.**Robo ...

  4. Python+Excel+Unittest+HTMLTestRunner实现数据驱动接口自动化测试(一)

    整个流程: 使用HTMLTestRunner的Run方法执行用例,用例调用Excel读取方法,将测试数据导入到unittest用例中执行,测试结果返回给HTMLTestRunner. 因为刚接触接口自 ...

  5. Python+requests+unittest+excel实现接口自动化测试框架

    一.框架结构:  工程目录 二.Case文件设计 三.基础包 base 3.1 封装get/post请求(runmethon.py) import requests import json class ...

  6. Python+requests+unittest+excel实现接口自动化测试框架(摘录)

    一.框架结构:  工程目录 二.Case文件设计 三.基础包 base 3.1 封装get/post请求(runmethon.py) 1 import requests 2 import json 3 ...

  7. Python+requests+unittest+excel实现接口自动化测试框架(转

    一.框架结构:工程目录 二.Case文件设计三.基础包 base 3.1 封装get/post请求(runmethon.py) import requests import json class Ru ...

  8. 使用python+requests+unittest实现接口自动化测试

    这两天一直在找直接用python做接口自动化的方法,在网上也搜了一些博客参考,今天自己动手试了一下. 一.整体结构 上图是项目的目录结构,下面主要介绍下每个目录的作用. Common:公共方法:主要放 ...

  9. python+requests+unittest执行自动化接口测试

    1.安装requests.xlrd.json.unittest库 <1>pip 命令安装: pip install requestspip install xlrdpip install ...

  10. python+requests+unittest 接口ddt测试

    以数据驱动的形式,将用例维护在py文件中 源码分析: 变量定义 publicParameters.py """ 公共参数 , 按照各公司实情,自行编写 "&qu ...

随机推荐

  1. 巅峰对决:英伟达 V100、A100/800、H100/800 GPU 对比

    近期,不论是国外的 ChatGPT,还是国内诸多的大模型,让 AIGC 的市场一片爆火.而在 AIGC 的种种智能表现背后,均来自于堪称天文数字的算力支持.以 ChatGPT 为例,据微软高管透露,为 ...

  2. mysql 表级锁之一lock table

    1.lock table t1 read: 1.1.当前线程: 读/写当前表/其他表: unlock tables; lock table t1 read; select * from t1; INS ...

  3. MySQL安装、卸载与初始化

    一.MySQL简介 1.MySQL是什么 MySQL 是一款安全.跨平台.高效的,并与 PHP.Java等主流编程语言紧密结合的关系型数据库管理系统.MySQL 的象征符号是一只名为 Sakila 的 ...

  4. C/C++ 开发SCM服务管理组件

    SCM(Service Control Manager)服务管理器是 Windows 操作系统中的一个关键组件,负责管理系统服务的启动.停止和配置.服务是一种在后台运行的应用程序,可以在系统启动时自动 ...

  5. Springboot 自动发送邮件

    完成Springboot配置发件邮箱,自动给其他邮箱发送邮件功能 一.创建springboot基础项目,引入依赖 <!-- Spring Boot 邮件依赖 --> <depende ...

  6. Mysql报:error while loading shared libraries libtinfo.so.5的解决办法

    版权声明:原创作品,谢绝转载!否则将追究法律责任. ----- 作者:kirin #.今天闲来无事,想在Anolis8的系统上装一个MySQL8.0玩.前期在安装和配置的过程中没有什么问题,但是在我想 ...

  7. Java实现相似结构表算法

    [产品需求] 对所有元数据进行分析,匹配出表字段相似度达到阈值的向相似结构表关系数据. 网上没有搜到相关算法实现,只能自己动手了. [算法实现] 简单点实现的话,可以轮询所有表,每张表都和其它表进行匹 ...

  8. 神经网络优化篇:详解偏差,方差(Bias /Variance)

    偏差,方差 注意到,几乎所有机器学习从业人员都期望深刻理解偏差和方差,这两个概念易学难精,即使自己认为已经理解了偏差和方差的基本概念,却总有一些意想不到的新东西出现.关于深度学习的误差问题,另一个趋势 ...

  9. java-导出pdf

    前言:   纯代码画pdf格式 <!-- iText PDF --> <dependency> <groupId>com.itextpdf</groupId& ...

  10. Go 语言为什么建议多使用切片,少使用数组?

    大家好,我是 frank,「Golang 语言开发栈」公众号作者. 01 介绍 在 Go 语言中,数组固定长度,切片可变长度:数组和切片都是值传递,因为切片传递的是指针,所以切片也被称为"引 ...