持续集成的回归测试框架

来源:互联网 发布:股城模拟炒股软件 编辑:程序博客网 时间:2024/06/13 21:45
  • 系统框图


  • 目录结构   
  • 代码
  • 第一步: 多线程运行测试用例,保存测试结果到数据库/result/regression.db:
         脚本执行命令:python readXml.py -t 4 config.txt
         参数-t 4:4个线程运行

    config.txt:配置文件
; 配置文件

[config]
; 记录测试向量的xml文件
run_xml=E:/python/regression/testcases/test1.xml

; 运行目录
run_dir=E:/python/regression/result

readXml.py:解析xml文件,获取测试用例的列表
#-*- coding:utf-8 -*-
import os,sys
import time
import getopt
import xml.etree.ElementTree as ET
from sqlAPI import SQLTest
import configparser
from readCase import runCaseByXml
from multiprocessing import Lock
from multiprocessing.pool import ThreadPool

def runTestByXml(xml_file,run_dir):
    '''read the xml file and check each testcases'''
    # 读取xml文件
    xml_root = ET.parse(xml_file)
    xml = xml_root.getroot()
    
    # 创建数据库
    db_file_path = os.path.join(run_dir,'regression.db')
    with open(db_file_path,'w'):
        db = SQLTest(path = db_file_path)
    # 创建信息表info
    create_info_sql = '''create table if not exists 'info' (\
                         'id' integer(16) NOT NULL,\
                         'version' nvarchar(64) NOT NULL,\
                         'begin_time' float NOT NULL,\
                         'end_time' float NOT NULL,\
                         'tables' nvarchar(128) DEFAULT NULL,\
                         'test_group' nvarchar(64) NOT NULL,\
                         PRIMARY KEY('id')\
                     )'''
    db.createTable(create_info_sql)
    insert_sql = '''INSERT INTO info values (?,?,?,?,?,?)'''
    info_id = db.rowCount('info') + 1
    date = time.strftime('%y%m%d',time.localtime(time.time()))
    data = [(info_id,'v_01',time.time(),'','','regression1')]
    db.insert(insert_sql,data)

    # 信息统计
    num_fail = 0       #失败的测试用例
    total_cases = 0    #测试用例的总数
    
    # 迭代group
    group_list = xml.find('common').find('groups').text.split(',')
    group_list = [item.strip() for item in group_list if item.strip()]
    tables = []
    tables_name = ''
    lock = Lock()
    for group in group_list:
        # 在SqlDB中创建表
        group_name = xml.find(group).find('name').text
        # 创建表单group_name
        if group_name == 'info':
            print('group name cannot be "info"')
            continue
        create_table_sql = '''create table if not exists '%s' (
                         'id' integer(16) NOT NULL,
                         'case_name' nvarchar(64) NOT NULL,
                         'begin_time' float NOT NULL,
                         'end_time' float NOT NULL,
                         'loss_packet' float(128) DEFAULT 0,
                         'note' nvarchar(64) DEFAULT NULL,
                         PRIMARY KEY('id')
                     )''' % group_name
        db.createTable(create_table_sql)
        # 取得本组所有的测试向量
        test_lists = []
        group_node = xml.find(group)
        if (group_node is None):
            print('Info : group {} not found'.fromat(group))
        for test_list in group_node.findall('file'):
            test_lists.append(test_list.text)
        
        # group的测试向量统计
        num_cases = len(test_lists)
        total_cases += num_cases
        
        # close db, because db object must be in same thread
        db.closedb()
        
        # 放入线程池,多线程运行每一个测试向量
        print('Using multiprocess ...')
        pool = ThreadPool(num_thread)
        raw_results = [pool.apply_async(runCaseByXml,(i+1,num_cases,test_lists[i],\
                      db_file_path,group_name,run_dir,lock)) for i in range(num_cases)]
        results = [r.get() for r in raw_results]
        pool.close()
        pool.join()
        
           db = SQLTest(path = db_file_path)
        #记录表明到数据库tables
        if tables == []:
            tables_name = group_name
        elif group_name not in tables:
            tables_name += ',{}'.format(group_name)
    
    #运行结束时间
    update_sql = 'UPDATE info SET end_time=? WHERE ID=?'
    data = [(time.time(),info_id)]
    db.updata(update_sql,data)

    #数据表
    update_sql = 'UPDATE info SET tables=? WHERE ID=?'
    data = [(tables_name,info_id)]
    db.updata(update_sql,data)
   
    #测试Fail统计
    print('{} total, {} failed'.format(total_cases,num_fail))


#main函数
if __name__ == '__main__':
    # 线程数量
    num_thread = 1
    try:
        opts,argv = getopt.getopt(sys.argv[1:],'t:')
    except getopt.GetoptError as err:
        print(err)
        sys.exit(1)
    
    for o,a in opts:
        if o == '-t':
            num_thread = int(a)
    
    # 解析配置文件
    config_file = argv[0]
    if os.path.isfile(config_file):
        # configparser解析配置文件
        config = configparser.ConfigParser() 
        config.read(config_file)
        # 如果参数多,建议封装成类
        xml_file = config['config']['run_xml']
        run_dir = config['config']['run_dir']
        runTestByXml(xml_file,run_dir)
    else:
         print('file {} not found'.format(config_file))

readCase.py:多线程调用软件可执行程序,执行测试用例,保存测试结果到数据库。

#-*- coding:utf-8 -*-
import os,sys
import time
import os,sys
import re
import shutil
import xml.etree.ElementTree as ET
from subprocess import Popen,PIPE,STDOUT,TimeoutExpired
from sqlAPI import SQLTest


def runCaseByXml(i,num_cases,xml_file,db_file_path,table_name,run_dir,lock):
    '''
    read the xml file and excute testcase
    @parameter i:本组测试向量的第i个
    @parameter num_cases:本组测试向量的总数
    @parameter test_list:测试向量
    @parameter db_file_path:数据库的文件路径
    @parameter table_name:数据库的表名称
    @parameter run_dir:测试向量的执行路径
    @parameter lock:多线程的lock
    '''
    print('{} of {} ***************'.format(i,num_cases))
    
    #xml file是否存在
    if not os.path.isfile(xml_file):
        print('File {} not found'.format(xml_file))
        return 1
        
    #读取xml文件内容
    xml_root = ET.parse(xml_file)
    xml = xml_root.getroot()
    #测试用例的id
    case_id = int(xml.find('id').text)
    case_name = xml.find('name').text
    ip = xml.find('ip').text
    ping_nums = xml.find('nums').text
    loss_packet = xml.find('loss').text
    
    #清空工作目录
    work_dir = ''.join([run_dir,'/reg',str(i)])
    if os.path.isdir(work_dir):
        shutil.rmtree(work_dir)
    os.mkdir(work_dir)
    
    #创建数据库db
    db = SQLTest(path = db_file_path)
    
    #创建运行测试用例的tcl
    run_tcl = ''.join([work_dir,'/MyScript.bat'])
    with open(run_tcl,'w') as fp:
        fp.write('ping {} -n {}'.format(ip,ping_nums))
    
    TIMEOUT_EXPIRED = 180 #单位:秒
    flag_timeout = False  #超时标识
    begin_time = time.time()
    
    #执行tcl
    try:
        p = Popen([run_tcl],stdout=PIPE,stderr=PIPE,cwd=work_dir)
        #等待子进程结束
        stdout_data,stderr_data = p.communicate(timeout=TIMEOUT_EXPIRED)  #超时退出
    except TimeoutExpired:
        p.kill()
        flag_timeout = True
        print('Info: run {} TimeoutExpired'.format(xml_file))
    
    end_time = time.time()
    
    #系统输出流stdout的编码方式
    stdout_encode = sys.stdout.encoding
    #写stdout_data到logfile
    stdout_log = os.path.join(work_dir,'run.log')
    with open(stdout_log,'w',encoding='utf-8') as fp:
        fp.write(stdout_data.decode(stdout_encode))
     
    #解析log文件
    with open(stdout_log,'r',encoding='utf-8') as fp:
        for line in fp:
            if '丢失' in line:
                impr_loss_packet = re.match('.*?丢失\s=\s(\d+)\s.*',line).group(1)


    insert_sql = 'INSERT INTO {} values(?,?,?,?,?,?)'.format(table_name)
    data = [(case_id,case_name,begin_time,end_time,impr_loss_packet,'')]
    with lock:
        db.insert(insert_sql,data)
    return 0 if (int(impr_loss_packet) <= int(loss_packet)) else 1
   
#自测
if __name__ == '__main__':
    pass

sqlAPI.py:数据库接口

代码参考以前的博客《python脚本sqlite3模块的应用

http://blog.csdn.net/zhanghs11/article/details/51338112


  • 第二步:生成测试报告/result/regression.html

脚本执行命令:python dbHtml.py db_file


dbHtml.py:生成Html报告

#-*- coding: utf-8 -*-

from pyh import *

from sqlAPI import SQLTest

import os,sys

import time


class ToHTML:

    def __init__(self,project = 'TEST', version = 'v1.0'):

        self.page = PyH('%s 测试报告' % project)

        self.project = project

        self.path = ''

        self.version = version

        self.start_time = time.time()

        self.end_time = ''

        self.group = ''

    

    #设置版本信息

    def setVersion(self,version=None):

        self.version = version

    

    def setPath(self,path=None):

        self.path = path


    def setGroup(self,group=None):

        self.group = group

        

    def setStartTime(self,start_time=None):

        if start_time is None:

            self.start_time = time.time()

        else:

            self.start_time = start_time

            

    def setEndTime(self,end_time):

        if end_time is None:

            self.end_time = time.time()

        else:

            self.end_time = end_time

            

    #设置表格的格式

    def tablecss(self, table = None, width = '600'):

        table.attributes['cellSpacing'] = 1

        table.attributes['cellPadding'] = 1

        table.attributes['border'] = 1

        table.attributes['borderColor'] = 'green'

        table.attributes['width'] = 'width'


    def tr_title_css(self, tr = None, color = '#B0C4DE'):

        tr.attributes['bgcolor'] = color


    #版本信息

    def genVersionInfo(self):

        self.page << div(b('版本信息'), id = 'mydiv')

        table_info = self.page << table()

        #表格宽度

        self.tablecss(table_info,'50%')

        

        version_tr_info = table_info << tr()

        self.tr_title_css(version_tr_info,'#5566CC')

        version_td_info = version_tr_info<<td('测试代码')

        value_tr_info = table_info << tr()

        td_dev = value_tr_info << td('版本:')

        td_dev << self.version

        td_dev << br()

        td_dev << '组别:'

        td_dev << self.group

        td_dev << br()

        td_dev << '开始:'

        td_dev << time.strftime('%y.%m.%d %X',time.localtime(self.start_time))

        td_dev << br()

        self.page << br()


    def genReport(self,filename='reg.html'):

        self.page << '结束'

        try:

            self.page << time.strftime('%y.%m.%d %X',time.localtime(self.end_time))

        except:

            self.page << 'unfinished'

            

        self.page << br()

        self.page.printOut(file_name)

    

    def setcontent(self,unit):

        value_tr_unit = self.tableGroup << tr()

        for unitOne in unit:

            value_tr_unit << td('%s' % unitOne)

            

    def genReport(self, filename = 'report.html'):

        print(filename)

        self.page << '结束:'

        try:

            self.page << 'finished'

        except:

            self.page << 'unfinished'

        self.page << br()

        self.page.printOut(filename)


def dbToHtml(pri_obj,db,file_path):

    #div 版本信息

    sql = 'SELECT * FROM info'

    table_rows = db.fetchAll(sql)

    for e in range(len(table_rows)):

        pri_obj.setVersion(table_rows[e][1])

        pri_obj.setStartTime(table_rows[e][2])

        pri_obj.setEndTime(table_rows[e][3])

        tables = table_rows[e][4]

        pri_obj.setGroup(table_rows[e][5])

        

    pri_obj.genVersionInfo()

    prj_obj.genReport(os.path.join(file_path,'regression.html'))


# self test

if __name__ == '__main__':

    prj_obj = ToHTML('TEST')


    # 命令提示

    if len(sys.argv) < 2:

        print('Info: command python dbHtml.py dbfile ...')

        sys.exit(0)

    

    db_file = sys.argv[1]

    file_dir,file_name = os.path.split(db_file)

    #打开数据库

    db = SQLTest(path = db_file)

    if os.path.isfile(db_file):

        dbToHtml(prj_obj,db,file_dir)

        pass

    else:

         print('file {} not found'.format(db_file))


生成的报告如下图所示:(报告不完整,后续完善)
    







0 0
原创粉丝点击