在稳定性测试中,将测试结果持续填加进入html报告

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

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

     先看看生成html_log.py文件

    

import shutil



try:
    import xml.etree.cElementTree as ET
except Exception:
    import xml.etree.ElementTree as ET

TYPE_OK = 0
TYPE_ERROR = -1
TYPE_FAIL = -2
TYPE_SKIP = -3


class InfoParser(object):

    def __init__(self, file_path, info):
        self.file_path = file_path
        self.info = info

    def get_state(self):
        if self.info.find('... ok') != -1:
            return TYPE_OK
        elif self.info.find('... ERROR') != -1:
            return TYPE_ERROR
        elif self.info.find('... FAIL') != -1:
            return TYPE_FAIL
        elif self.info.find('... SKIP') != -1:
            return TYPE_SKIP

    def get_detail(self):
        if self.get_state() == 0:
            return ''
        else:
            offset = self.info.find('Traceback')
            if offset != -1:
                temp = self.info[offset:]
                return temp
            else:
                return ''

    def get_class_name(self):
        temp = self.get_testcase_name()
        offset = temp.rfind('.')
        if offset != -1:
            temp = temp[0: offset]
            return temp
        else:
            return ''

    def get_testcase_name(self):
        offset = self.info.find(' ...')
        if offset != -1:
            temp = self.info[0: offset]
            return temp
        return ''

    def create_new_overview_node(self, root, state, class_name):
        new_node = ET.Element('tr')
        root.insert(len(root.getchildren()) - 1, new_node)
        # testcase name
        sub_node = ET.SubElement(new_node, 'td')
        sub_node.text = class_name

        for x in range(5):
            sub_node = ET.SubElement(new_node, 'td')
            sub_node.text = '0'

        return new_node

    def add_overview_node_detail(self, item, state):
        item[5].text = str(int(item[5].text) + 1)
        if state == TYPE_FAIL:
            item[1].text = str(int(item[1].text) + 1)
            item[1].set('class', 'failed')
        elif state == TYPE_ERROR:
            item[2].text = str(int(item[2].text) + 1)
            item[2].set('class', 'failed')
        elif state == TYPE_SKIP:
            item[3].text = str(int(item[3].text) + 1)
        elif state == TYPE_OK:
            item[4].text = str(int(item[4].text) + 1)

    def add_to_overview(self, root, state, class_name):
        iter = root.getchildren()
        for item in iter:
            if item[0].text == class_name:
                self.add_overview_node_detail(item, state)
                # handle total
                total_item = root[len(root.getchildren()) - 1]
                self.add_overview_node_detail(total_item, state)
                return

        new_item = self.create_new_overview_node(root, state, class_name)
        self.add_overview_node_detail(new_item, state)
        # handle total
        total_item = root[len(root.getchildren()) - 1]
        self.add_overview_node_detail(total_item, state)

    def add_to_failure(self, root):
        new_item = ET.SubElement(root, 'section')
        # name
        sub_item = ET.SubElement(new_item, 'h3')
        sub_item.text = self.get_testcase_name()
        # details
        sub_item = ET.SubElement(new_item, 'div')
        sub_item.set('class', 'test-details')
        detail_sub_item = ET.SubElement(sub_item, 'h4')
        detail_sub_item.text = 'Detail'
        detail_sub_item = ET.SubElement(sub_item, 'pre')
        detail_sub_item.text = self.get_detail()

    def add_to_all_tests(self, root, state):
        new_item = ET.SubElement(root, 'li')
        sub_item = ET.SubElement(new_item, 'a')
        sub_item.text = self.get_testcase_name()
        if state == TYPE_FAIL or state == TYPE_ERROR:
            sub_item.set('class', 'failed')
        else:
            sub_item.set('class', 'success')

    def write_log(self):
        ret = self.get_state()

        tree = ET.parse(self.file_path)
        root = tree.getroot()

        # add overview
        self.add_to_overview(
            root.find('body').find('overview').find('section').find('table'),
            ret, self.get_class_name())

        # add fail view
        if ret == TYPE_FAIL or ret == TYPE_ERROR:
            self.add_to_failure(
                root.find('body').find('failure_details').
                find('section').find('div'))

        # add all tests
        self.add_to_all_tests(
            root.find('body').find('all_tests').find('section').find('ul'),
            ret)

        tree.write(self.file_path)


def create_log_from_file(path, souce):
    shutil.copyfile(souce, path)


def add_log(path, info):
    info_parser = InfoParser(path, info)
    info_parser.write_log()


def format_testrunner_info(test, info, state):
    result = ''
    # handle name
    name = str(test)
    offset_b = name.find('(')
    offset_e = name.find(')')
    testcase_name = 'no name'
    if offset_b != -1 and offset_e != -1:
        testcase_name = name[offset_b + 1: offset_e]
        offset_e = name.find(' (')
        if offset_e != -1:
            testcase_name += '.'
            testcase_name += name[0: offset_e]
    result = testcase_name

    if state == TYPE_OK:
        result += ' ... ok

'
    elif state == TYPE_ERROR:
        result += ' ... ERROR

'
    elif state == TYPE_FAIL:
        result += ' ... FAIL

'
    elif state == TYPE_SKIP:
        result += ' ... SKIP

'

    result += info

    return result


def add_testrunner_log(path, result, test_name):
    # success
    if result.wasSuccessful():
        info = format_testrunner_info(test_name, 'Ran 1 test', TYPE_OK)
        #info = format_testrunner_info(name,'Ran 1 test',TYPE_OK)
        add_log(path, info)

    # fail
    for test, err in result.failures:
        info = format_testrunner_info(test, err, TYPE_FAIL)
        add_log(path, info)

    # error
    for name, err in result.errors:
        info = format_testrunner_info(name, err, TYPE_ERROR)
        add_log(path, info)

    # skip
    for test, reason in result.skipped:
        info = format_testrunner_info(test, reason, TYPE_SKIP)
        add_log(path, info)

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

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

<!DOCTYPE html>
<html>
<head>
    <title>Unit Test Report</title>
    <meta http-equiv="Content-Type" content="text/html; charset=UTF-8"/>

<style>
body {
    font-family: Calibri, "Trebuchet MS", sans-serif;
}
* {
    word-break: break-all;
}
table, td, th, .dataid {
    border: 1px solid #aaa;
    border-collapse: collapse;
    background: #fff;
}
section {
    background: rgba(0, 0, 0, 0.05);
    margin: 2ex;
    padding: 1ex;
    border: 1px solid #999;
    border-radius: 5px;
}
h1 {
    font-size: 130%;
}
h2 {
    font-size: 120%;
}
h3 {
    font-size: 100%;
}
h4 {
    font-size: 85%;
}
h1, h2, h3, h4, a[href] {
    cursor: pointer;
    color: #0074d9;
    text-decoration: none;
}
h3 strong, a.failed {
    color: #ff4136;
}
.failed {
    color: #ff4136;
}
a.success {
    color: #3d9970;
}
pre {
    font-family: 'Consolas', 'Deja Vu Sans Mono',
                 'Bitstream Vera Sans Mono', 'Monaco',
                 'Courier New', monospace;
}

.test-details,
.traceback {
    display: none;
}
section:target .test-details {
    display: block;
}

</style>
</head>
<body>
    <overview>
        <h1>Overview</h1>
        <section>
            <table>
                <tr>
                    <th>Class</th>
                    <th class="failed">Fail</th>
                    <th class="failed">Error</th>
                    <th>Skip</th>
                    <th>Success</th>
                    <th>Total</th>
                </tr>
                <tr>
                <td><strong>Total</strong></td>
                <td>0</td>
                <td>0</td>
                <td>0</td>
                <td>0</td>
                <td>0</td>
            </tr>
            </table>
        </section>
    </overview>
    <failure_details>
        <h1>Failure details</h1>
            <section>
                <h2>Failure details</h2>
                <div>
                </div>
            </section>
    </failure_details>
    <all_tests>
        <h1>All tests</h1>
            <section>
                <h2>all tests</h2>
                <ul>
                </ul>
            </section>
    </all_tests>
</body>
<script>
    Array.prototype.forEach.call(document.querySelectorAll('h1, h2, h3, h4'), function(el) {
        el.addEventListener('click', function() {
            el.nextElementSibling.style.display = document.defaultView.getComputedStyle(el.nextElementSibling).display == 'none' ? 'block' : 'none';
        })
    })
</script>
View Code

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

import unittest
import time
import sys
import html_log
import os
import re
import random


class test(unittest.TestCase):
    def setUp(self):
        pass
    
    def test_0001(self):
        
        assert 1==1
    
    def test_0002(self):
        
        assert 2==2

if __name__=='__main__':
    suite=unittest.TestLoader().loadTestsFromTestCase(test)
    testcases=list()
    listcasedir='.'
    testunit=unittest.TestSuite()
  
    
    #选择case
    for test_case in suite:
        print test_case
        f=re.match("(test_.*) (__main__.test)",str(test_case))
        tt=f.group(1)
        testcases.append(test_case)
    
    test = random.choice(testcases)
    mySuite = unittest.TestSuite()
    mySuite.addTest(test)
    result = unittest.TextTestRunner().run(mySuite)
    
    #这里xx.html就是对应的空模板
if not os.path.exists('test_aa.html'): html_log.create_log_from_file('test_aa.html', 'xx.html')
#将结果持续加入到对应的html报告中 print test html_log.add_testrunner_log('test_aa.html', result, test)

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

原文地址:https://www.cnblogs.com/landhu/p/6284873.html