# -*- coding:utf-8 -*- # 日期:2019年9月9日 # 作者:Wang WenJie # 版本:1.2.1 # 说明:用于TestLink导出后的Xml文件转成Excel文件 import xlwt from xml.dom.minidom import parse, Node import os import re import json class XmlToExcel(object): ''' TestLink导出的xml文件转Excel 构造字典树读取xml文件 ''' def __init__(self): # 全局字典树 self.__xml_tree = [] def __getText(self, node): ''' 获取节点的文本内容 Args: node: 节点 Returns: 节点中的文本 ''' if node is None: return '' if node.nodeType in [Node.TEXT_NODE, Node.CDATA_SECTION_NODE]: # return node.nodeValue return node.wholeText else: return '' def __get_importance(self, num): ''' 重要性序号转换成对应的中文 ''' if num == '1': return '低' elif num == '2': return '中' elif num == '3': return '高' return num def __get_exec_type(self, num): ''' 执行方式序号转换成对应的中文 ''' if num == '1': return '手工' elif num == '2': return '自动的' return num def __read_cases(self, case): ''' 读单个testcase节点,并返回字典 ''' case_dic = {} # 节点名称,即对应的标题 case_dic['casename'] = case.getAttribute('name') case_row = 0 for child in case.childNodes: # 如果遇到换行会读出TEXT_NODE,所以加上判断 if child.nodeType == Node.ELEMENT_NODE: # 摘要和前提 if child.tagName in ['summary', 'preconditions']: case_dic[child.tagName] = self.__getText(child.firstChild) elif child.tagName == 'importance': # 重要性 num = self.__getText(child.firstChild) case_dic[child.tagName] = self.__get_importance(num) elif child.tagName == 'execution_type': # 执行方式 num = self.__getText(child.firstChild) case_dic[child.tagName] = self.__get_exec_type(num) elif child.tagName == 'steps': # 步骤 case_dic['steps'] = [] for steps in child.getElementsByTagName('step'): step_dic = {} case_row = case_row + 1 for step in steps.childNodes: if step.nodeType == Node.ELEMENT_NODE: if step.tagName in ['actions', 'expectedresults']: step_dic[step.tagName] = self.__getText(step.firstChild) case_dic['steps'].append(step_dic) # 记录有多少步 case_dic['row'] = case_row return case_dic def __read_suites(self, root): ''' 读模块下的testsuite ''' suites = [] for testsuite in root.getElementsByTagName('testsuite'): module_tree = {} # 模块名 module = testsuite.getAttribute('name') module_tree['module'] = module module_tree['testcase'] = [] module_row = 0 for case in testsuite.getElementsByTagName('testcase'): case_dic = self.__read_cases(case) module_row = module_row + case_dic['row'] module_tree['testcase'].append(case_dic) # 记录模块占多少行 module_tree['row'] = module_row suites.append(module_tree) return suites def __read_single(self, root): ''' 读单节点用例 ''' suites = [] module_tree = {} module_tree['module'] = '' module_tree['testcase'] = [] module_row = 0 testcase = root.getElementsByTagName('testcase') for case in testcase: case_dic = self.__read_cases(case) module_row = module_row + case_dic['row'] # 记录模块占多少行 module_tree['row'] = module_row suites.append(module_tree) return suites def read_xml(self, xml_files): ''' 读xml文件主要方法 Args: xml_files: xml文件路径 ''' # 统计 result = {} result['success'] = 0 result['fail'] = 0 for file in xml_files: xml_dic = {} try: dom = parse(file) except: print('ERROR: 载入文件失败[{}]'.format(file)) result['fail'] = result['fail'] + 1 continue # 选择根节点 root = dom.documentElement if root.nodeName == 'testsuite': # 多用例集 suites = self.__read_suites(root) # 一级模块名,即sheet页名 sheet_name = root.getAttribute('name') if sheet_name.strip() == '': raise ValueError("xml文件有误, 请检查testsuite的name属性, 不能为空."); elif root.nodeName == 'testcases': # 单个用例 suites = self.__read_single(root) sheet_name = 'single' else: print('ERROR: 文件不是TestLink标准格式') result['fail'] = result['fail'] + 1 continue xml_dic[sheet_name] = suites xml_dic['sheet_name'] = sheet_name self.__xml_tree.append(xml_dic) result['success'] = result['success'] + 1 return result def set_style(self, height = 12, align = 'center'): ''' 设置写入单元格样式,包括文字大小,对齐方式 ''' # 字体样式 font = xlwt.Font() font.height = height * 20 font.name = '宋体' # 对齐方式 alignment = xlwt.Alignment() # 居中 if align == 'center': alignment.horz = xlwt.Alignment.HORZ_CENTER elif align == 'left': # 左对齐 alignment.horz = xlwt.Alignment.HORZ_LEFT alignment.vert = xlwt.Alignment.VERT_CENTER alignment.wrap = 1 # 自动换行 style = xlwt.XFStyle() style.alignment = alignment style.font = font return style def __auto_break(self, text): ''' 将html标记清除,并替换成对应的换行符 ''' symbol = ('

', '

', ' ') for s in symbol: text = re.sub(s, '', text, flags=re.M|re.I|re.S) # 替换为换行 text = re.sub(r'<.+[/]{1}>|<[/]{1}.>|
', r'\n', text, flags=re.M|re.I|re.S) return text.strip() def write_excel(self, file_name): ''' 将构造的字典树写入Excel文件中 ''' workbook = xlwt.Workbook(encoding='utf-8') size = 100 # 标题 titles = (('模块', 35), ('用例标题', 50), ('重要性', 25), ('执行方式', 27), ('摘要', 80), ('前提', 80), ('步骤', 160), ('结果', 160)) result = {} result['module'] = 0 result['case'] = 0 for sheet in self.__xml_tree: sheet_name = sheet.get('sheet_name') # 插入sheet名称 worksheet = workbook.add_sheet(sheet_name) col = 0 row = 1 end = 0 title_row = 1 title_end = 0 # 写入标题 for t, w in titles: width = w * size worksheet.write(0, col, t, self.set_style(11)) worksheet.col(col).width = width col = col + 1 # 写模块 for module in sheet.get(sheet_name): # 写模块名 end = row + int(module.get('row')) - 1 worksheet.write_merge(row, end, 0, 0, module.get('module'), self.set_style(12)) row = end + 1 # 更新模块列 center = self.set_style() left = self.set_style(align='left') # 写用例 for case in module.get('testcase'): title_end = int(case.get('row')) + title_row - 1 worksheet.write_merge(title_row, title_end, 1, 1, case.get('casename'), center) worksheet.write_merge(title_row, title_end, 2, 2, case.get('importance'), center) worksheet.write_merge(title_row, title_end, 3, 3, case.get('execution_type'), center) summary = case.get('summary') worksheet.write_merge(title_row, title_end, 4, 4, self.__auto_break(summary), left) prec = case.get('preconditions') worksheet.write_merge(title_row, title_end, 5, 5, self.__auto_break(prec), left) # 步骤 step_row = title_row for step in case.get('steps'): actions = step.get('actions') worksheet.write(step_row, 6, self.__auto_break(actions), left) results = step.get('expectedresults') worksheet.write(step_row, 7, self.__auto_break(results), left) step_row = step_row + 1 title_row = title_end + 1 # 用例数+1 result['case'] = result['case'] + 1 # 模块数+1 result['module'] = result['module'] + 1 # 写入Excel workbook.save(file_name) return result if __name__ == "__main__": file = [os.path.join(os.getcwd(), 'test.xml')] excel = os.path.join(os.getcwd(), 'test.xls') dom = XmlToExcel() dom.read_xml(file) dom.write_excel(excel)