公司需要设计一个稳定性测试,就是一直持续的跑不同的用例,直到人为停止,用例基本完成,基本框架思路就是随机选择一个testcase,跑完后输出结果。但存在一个问题,现在的unittest或nose测试报告都是测完所有case后再输出html报告,再次运行,又生成新的,没法再原来的报告中再填加结果。

就这样问题,基本解决思路就是直接写个生成html报告的模块,然后再次每次结果加入。正好最近在看tempest的东西,里面有个这样的模块,那就不重复造轮子了,直接拿过来用吧。

先看看生成html_log.py文件

  1. import shutil
  2.  
  3. try:
  4. import xml.etree.cElementTree as ET
  5. except Exception:
  6. import xml.etree.ElementTree as ET
  7.  
  8. TYPE_OK = 0
  9. TYPE_ERROR = -1
  10. TYPE_FAIL = -2
  11. TYPE_SKIP = -3
  12.  
  13. class InfoParser(object):
  14.  
  15. def __init__(self, file_path, info):
  16. self.file_path = file_path
  17. self.info = info
  18.  
  19. def get_state(self):
  20. if self.info.find('... ok') != -1:
  21. return TYPE_OK
  22. elif self.info.find('... ERROR') != -1:
  23. return TYPE_ERROR
  24. elif self.info.find('... FAIL') != -1:
  25. return TYPE_FAIL
  26. elif self.info.find('... SKIP') != -1:
  27. return TYPE_SKIP
  28.  
  29. def get_detail(self):
  30. if self.get_state() == 0:
  31. return ''
  32. else:
  33. offset = self.info.find('Traceback')
  34. if offset != -1:
  35. temp = self.info[offset:]
  36. return temp
  37. else:
  38. return ''
  39.  
  40. def get_class_name(self):
  41. temp = self.get_testcase_name()
  42. offset = temp.rfind('.')
  43. if offset != -1:
  44. temp = temp[0: offset]
  45. return temp
  46. else:
  47. return ''
  48.  
  49. def get_testcase_name(self):
  50. offset = self.info.find(' ...')
  51. if offset != -1:
  52. temp = self.info[0: offset]
  53. return temp
  54. return ''
  55.  
  56. def create_new_overview_node(self, root, state, class_name):
  57. new_node = ET.Element('tr')
  58. root.insert(len(root.getchildren()) - 1, new_node)
  59. # testcase name
  60. sub_node = ET.SubElement(new_node, 'td')
  61. sub_node.text = class_name
  62.  
  63. for x in range(5):
  64. sub_node = ET.SubElement(new_node, 'td')
  65. sub_node.text = '0'
  66.  
  67. return new_node
  68.  
  69. def add_overview_node_detail(self, item, state):
  70. item[5].text = str(int(item[5].text) + 1)
  71. if state == TYPE_FAIL:
  72. item[1].text = str(int(item[1].text) + 1)
  73. item[1].set('class', 'failed')
  74. elif state == TYPE_ERROR:
  75. item[2].text = str(int(item[2].text) + 1)
  76. item[2].set('class', 'failed')
  77. elif state == TYPE_SKIP:
  78. item[3].text = str(int(item[3].text) + 1)
  79. elif state == TYPE_OK:
  80. item[4].text = str(int(item[4].text) + 1)
  81.  
  82. def add_to_overview(self, root, state, class_name):
  83. iter = root.getchildren()
  84. for item in iter:
  85. if item[0].text == class_name:
  86. self.add_overview_node_detail(item, state)
  87. # handle total
  88. total_item = root[len(root.getchildren()) - 1]
  89. self.add_overview_node_detail(total_item, state)
  90. return
  91.  
  92. new_item = self.create_new_overview_node(root, state, class_name)
  93. self.add_overview_node_detail(new_item, state)
  94. # handle total
  95. total_item = root[len(root.getchildren()) - 1]
  96. self.add_overview_node_detail(total_item, state)
  97.  
  98. def add_to_failure(self, root):
  99. new_item = ET.SubElement(root, 'section')
  100. # name
  101. sub_item = ET.SubElement(new_item, 'h3')
  102. sub_item.text = self.get_testcase_name()
  103. # details
  104. sub_item = ET.SubElement(new_item, 'div')
  105. sub_item.set('class', 'test-details')
  106. detail_sub_item = ET.SubElement(sub_item, 'h4')
  107. detail_sub_item.text = 'Detail'
  108. detail_sub_item = ET.SubElement(sub_item, 'pre')
  109. detail_sub_item.text = self.get_detail()
  110.  
  111. def add_to_all_tests(self, root, state):
  112. new_item = ET.SubElement(root, 'li')
  113. sub_item = ET.SubElement(new_item, 'a')
  114. sub_item.text = self.get_testcase_name()
  115. if state == TYPE_FAIL or state == TYPE_ERROR:
  116. sub_item.set('class', 'failed')
  117. else:
  118. sub_item.set('class', 'success')
  119.  
  120. def write_log(self):
  121. ret = self.get_state()
  122.  
  123. tree = ET.parse(self.file_path)
  124. root = tree.getroot()
  125.  
  126. # add overview
  127. self.add_to_overview(
  128. root.find('body').find('overview').find('section').find('table'),
  129. ret, self.get_class_name())
  130.  
  131. # add fail view
  132. if ret == TYPE_FAIL or ret == TYPE_ERROR:
  133. self.add_to_failure(
  134. root.find('body').find('failure_details').
  135. find('section').find('div'))
  136.  
  137. # add all tests
  138. self.add_to_all_tests(
  139. root.find('body').find('all_tests').find('section').find('ul'),
  140. ret)
  141.  
  142. tree.write(self.file_path)
  143.  
  144. def create_log_from_file(path, souce):
  145. shutil.copyfile(souce, path)
  146.  
  147. def add_log(path, info):
  148. info_parser = InfoParser(path, info)
  149. info_parser.write_log()
  150.  
  151. def format_testrunner_info(test, info, state):
  152. result = ''
  153. # handle name
  154. name = str(test)
  155. offset_b = name.find('(')
  156. offset_e = name.find(')')
  157. testcase_name = 'no name'
  158. if offset_b != -1 and offset_e != -1:
  159. testcase_name = name[offset_b + 1: offset_e]
  160. offset_e = name.find(' (')
  161. if offset_e != -1:
  162. testcase_name += '.'
  163. testcase_name += name[0: offset_e]
  164. result = testcase_name
  165.  
  166. if state == TYPE_OK:
  167. result += ' ... ok\n\n'
  168. elif state == TYPE_ERROR:
  169. result += ' ... ERROR\n\n'
  170. elif state == TYPE_FAIL:
  171. result += ' ... FAIL\n\n'
  172. elif state == TYPE_SKIP:
  173. result += ' ... SKIP\n\n'
  174.  
  175. result += info
  176.  
  177. return result
  178.  
  179. def add_testrunner_log(path, result, test_name):
  180. # success
  181. if result.wasSuccessful():
  182. info = format_testrunner_info(test_name, 'Ran 1 test', TYPE_OK)
  183. #info = format_testrunner_info(name,'Ran 1 test',TYPE_OK)
  184. add_log(path, info)
  185.  
  186. # fail
  187. for test, err in result.failures:
  188. info = format_testrunner_info(test, err, TYPE_FAIL)
  189. add_log(path, info)
  190.  
  191. # error
  192. for name, err in result.errors:
  193. info = format_testrunner_info(name, err, TYPE_ERROR)
  194. add_log(path, info)
  195.  
  196. # skip
  197. for test, reason in result.skipped:
  198. info = format_testrunner_info(test, reason, TYPE_SKIP)
  199. add_log(path, info)

  使用非常简单,使用对应的空报告模板,使生成的结果调用add_testrunner_log写入空模板中,最后就可以持续生成报告了

空模板见下面的html代码,copy下来后存成html文件即可使用

  1. <!DOCTYPE html>
  2. <html>
  3. <head>
  4. <title>Unit Test Report</title>
  5. <meta http-equiv="Content-Type" content="text/html; charset=UTF-8"/>
  6.  
  7. <style>
  8. body {
  9. font-family: Calibri, "Trebuchet MS", sans-serif;
  10. }
  11. * {
  12. word-break: break-all;
  13. }
  14. table, td, th, .dataid {
  15. border: 1px solid #aaa;
  16. border-collapse: collapse;
  17. background: #fff;
  18. }
  19. section {
  20. background: rgba(0, 0, 0, 0.05);
  21. margin: 2ex;
  22. padding: 1ex;
  23. border: 1px solid #999;
  24. border-radius: 5px;
  25. }
  26. h1 {
  27. font-size: 130%;
  28. }
  29. h2 {
  30. font-size: 120%;
  31. }
  32. h3 {
  33. font-size: 100%;
  34. }
  35. h4 {
  36. font-size: 85%;
  37. }
  38. h1, h2, h3, h4, a[href] {
  39. cursor: pointer;
  40. color: #0074d9;
  41. text-decoration: none;
  42. }
  43. h3 strong, a.failed {
  44. color: #ff4136;
  45. }
  46. .failed {
  47. color: #ff4136;
  48. }
  49. a.success {
  50. color: #3d9970;
  51. }
  52. pre {
  53. font-family: 'Consolas', 'Deja Vu Sans Mono',
  54. 'Bitstream Vera Sans Mono', 'Monaco',
  55. 'Courier New', monospace;
  56. }
  57.  
  58. .test-details,
  59. .traceback {
  60. display: none;
  61. }
  62. section:target .test-details {
  63. display: block;
  64. }
  65.  
  66. </style>
  67. </head>
  68. <body>
  69. <overview>
  70. <h1>Overview</h1>
  71. <section>
  72. <table>
  73. <tr>
  74. <th>Class</th>
  75. <th class="failed">Fail</th>
  76. <th class="failed">Error</th>
  77. <th>Skip</th>
  78. <th>Success</th>
  79. <th>Total</th>
  80. </tr>
  81. <tr>
  82. <td><strong>Total</strong></td>
  83. <td>0</td>
  84. <td>0</td>
  85. <td>0</td>
  86. <td>0</td>
  87. <td>0</td>
  88. </tr>
  89. </table>
  90. </section>
  91. </overview>
  92. <failure_details>
  93. <h1>Failure details</h1>
  94. <section>
  95. <h2>Failure details</h2>
  96. <div>
  97. </div>
  98. </section>
  99. </failure_details>
  100. <all_tests>
  101. <h1>All tests</h1>
  102. <section>
  103. <h2>all tests</h2>
  104. <ul>
  105. </ul>
  106. </section>
  107. </all_tests>
  108. </body>
  109. <script>
  110. Array.prototype.forEach.call(document.querySelectorAll('h1, h2, h3, h4'), function(el) {
  111. el.addEventListener('click', function() {
  112. el.nextElementSibling.style.display = document.defaultView.getComputedStyle(el.nextElementSibling).display == 'none' ? 'block' : 'none';
  113. })
  114. })
  115. </script>

使用unittest写个示范代码如下:

  1. import unittest
  2. import time
  3. import sys
  4. import html_log
  5. import os
  6. import re
  7. import random
  8.  
  9. class test(unittest.TestCase):
  10. def setUp(self):
  11. pass
  12.  
  13. def test_0001(self):
  14.  
  15. assert 1==1
  16.  
  17. def test_0002(self):
  18.  
  19. assert 2==2
  20.  
  21. if __name__=='__main__':
  22. suite=unittest.TestLoader().loadTestsFromTestCase(test)
  23. testcases=list()
  24. listcasedir='.'
  25. testunit=unittest.TestSuite()
  26.  
  27. #选择case
  28. for test_case in suite:
  29. print test_case
  30. f=re.match("(test_.*) \(__main__.test\)",str(test_case))
  31. tt=f.group(1)
  32. testcases.append(test_case)
  33.  
  34. test = random.choice(testcases)
  35. mySuite = unittest.TestSuite()
  36. mySuite.addTest(test)
  37. result = unittest.TextTestRunner().run(mySuite)
  38.  
  39. #这里xx.html就是对应的空模板
    if not os.path.exists('test_aa.html'):
  40. html_log.create_log_from_file('test_aa.html', 'xx.html')
  41. #将结果持续加入到对应的html报告中
  42. print test
  43. html_log.add_testrunner_log('test_aa.html', result, test)

结果如下,测试一直在进行,结果就一直会持续输入

在稳定性测试中,将测试结果持续填加进入html报告的更多相关文章

  1. 使用强大的 Mockito 测试框架来测试你的代码

    原文链接 : Unit tests with Mockito - Tutorial 译文出自 : 掘金翻译计划 译者 : edvardhua 校对者: hackerkevin, futureshine ...

  2. web测试中的测试点和测试方法总结

    测试是一种思维,包括情感思维和智力思维,情感思维主要体现在一句俗语:思想决定行动上(要怀疑一切),智力思维主要体现在测试用例的设计上.具有了这样的思想,就会找出更多的bug.   一.输入框 1.字符 ...

  3. IOS(SystemConfiguration)框架中关于测试连接网络状态相关方法

    1. 在SystemConfiguration.famework中提供和联网相关的function, 可用来检查网络连接状态. 2. SC(SystemConfiguration)框架中关于测试连接网 ...

  4. iOS开发中的测试框架

    转载作者:@crespoxiao 我们为什么要用测试框架呢?当然对项目开发有帮助了,但是业内现状是经常赶进度,所以TDD还是算了吧,BDD就测测数据存取和重要环节,这很重要,一次性跑完测试单元检查接口 ...

  5. iOS开发中的测试框架 (转载)

      作者:CrespoXiao授权 地址:http://www.jianshu.com/p/7e3f197504c1 我们为什么要用测试框架呢?当然对项目开发有帮助了,但是业内现状是经常赶进度,所以T ...

  6. python多线程在渗透测试中的应用

    难易程度:★★★ 阅读点:python;web安全; 文章作者:xiaoye 文章来源:i春秋 关键字:网络渗透技术 前言 python是门简单易学的语言,强大的第三方库让我们在编程中事半功倍,今天, ...

  7. 测试笔试单选题(持续更新ing)

    1.在GB/T17544中,软件包质量要求包括三部分,即产品描述要求._____.程 序和数据要求.( A ) A.用户文档要求 B.系统功能要求 C.设计要求说明 D.软件配置要求 2.软件的六大质 ...

  8. 专访|HPE测试中心总监徐盛:测试新思维-DevOps,持续测试,更敏捷,更快速

    2016年7月22日,「HPE&msup软件技术开放日」将在上海浦东新区,张江高科技园区纳贤路799号科荣大厦小楼2楼举办,msup携手HPE揭秘全球测试中心背后的12条技术实践. 徐盛:HP ...

  9. 详述MySQL服务在渗透测试中的利用

    本文作者:i春秋签约作家——Binghe 致力于书写ichunqiu社区历史上最长篇最细致最真实的技术复现文章. 文章目录: MySQL之UDF提权 MySQL之MOF提权 MySQL之常规写启动项提 ...

随机推荐

  1. ARC介绍

    从Ray Wenderlich的教程中截取了一小段作为对objective c中ARC的介绍,讲得比较清晰,原文有丰富的例子,见此 它是怎么工作的 你大概已经熟悉如何手工管理内存了, 就像这样:如果你 ...

  2. tomcat设置debug模式

    1.设置 编辑catalina.bat,在 rem Guess CATALINA_HOME if not definedset "CURRENT_DIR=%cd%"if not & ...

  3. LINE@生活圈招募好友秘笈

    什么是「获得更多好友」页面? 您可从  LINE@ app >管理>获得更多好友  进入此页面. ▼ 「获得更多好友」新介面中,募集好友的四大秘诀 秘诀一.「以社群网站或电子邮件分享」 • ...

  4. showModalDialog介绍

    基本介绍:          showModalDialog()         (IE 4+ 支持)          showModelessDialog()      (IE 5+ 支持)    ...

  5. python使用paramiko自动化部署linux程序

    使用paramiko模块,比os模块和command模块更加的兼容各种环境.后面两个只能在服务器本机 执行,此模块写得python文件无论是在本地还是服务器本身上运行,都能兼容. paramiko模块 ...

  6. Gibbs采样

    (学习这部分内容大约需要50分钟) 摘要 Gibbs采样是一种马尔科夫连蒙特卡洛(Markov Chain Monte Carlo, MCMC)算法, 其中每个随机变量从给定剩余变量的条件分布迭代地重 ...

  7. Dubbo -- 系统学习 笔记 -- 示例 -- 线程模型

    Dubbo -- 系统学习 笔记 -- 目录 示例 想完整的运行起来,请参见:快速启动,这里只列出各种场景的配置方式 线程模型 事件处理线程说明 如果事件处理的逻辑能迅速完成,并且不会发起新的IO请求 ...

  8. WPF路由事件学习(一)

    路由事件与一般事件的区别在于:路由事件是一种用于元素树的事件,当路由事件触发后,它可以向上或向下遍历可视树和逻辑树,他用一种简单而持久的方式在每个元素上触发,而不需要任何定制的代码(如果用传统的方式实 ...

  9. ios开发之--VC的生命周期

    当一个视图控制器被创建,并在屏幕上显示的时候. 代码的执行顺序 1. alloc                                   创建对象,分配空间 2.init (initWit ...

  10. ios8 UITableView设置 setSeparatorInset:UIEdgeInsetsZero不起作用的解决办法

    在ios7中,UITableViewCell左侧会有默认15像素的空白.这时候,设置setSeparatorInset:UIEdgeInsetsZero 能将空白去掉. 但是在ios8中,设置setS ...