提交testlink转换工具

This commit is contained in:
halliday 2025-11-11 19:43:42 +08:00
parent e50111aaad
commit c9605ad3b5
16 changed files with 2029 additions and 0 deletions

View File

@ -0,0 +1,435 @@
# -*- coding:utf-8 -*-
# 日期2019-8-14
# 作者Wang WenJie
# 版本1.2.2
# 说明Excel转Xml用于TestLink导入测试案例
#*******************************************
# 修改说明:
# 2019-9-10修复CDATA类型文件转换后无法被读取
# 修改换行标记
# 2019-10-22修复模块中间出现空白行解析不全
#*******************************************
import xlrd
import xml.dom.minidom as minidom
import re
import os
import sys
reload(sys)
sys.setdefaultencoding('utf-8') #解决写入文件时候编码错误
class ExcelToXml(object):
"""
Excel转Xml用于TestLink导入测试案例
构造字典树遍历写入xml文件
"""
def __init__(self, file_name):
try:
self.__xl = xlrd.open_workbook(file_name, formatting_info = True)
except NotImplementedError:
raise ValueError(u'不支持xlsx格式文件')
self.__xml_tree = {} # 全局字典树
self.__result = [0, 0, 0] #sheet页模块测试案例
_, self.__filename = os.path.split(file_name)
def __get_importance(self, zh_str):
'''
重要性中文转对应数字不存在返回原字符
Args:
zh_str: 待转换字符
'''
if zh_str == u'':
return '2'
elif zh_str == u'':
return '1'
elif zh_str == u'':
return '3'
# 都不存在返回原字符
return zh_str
def __get_exec_type(self, zh_str):
'''
执行方式中文转对应数字不存在返回原字符
Args:
zh_str: 待转换字符
'''
if zh_str == u'手工':
return '1'
elif zh_str == u'自动的':
return '2'
# 都不存在返回原字符
return zh_str
def __get_modules(self, col):
'''
从各个sheet页中的模块名开始遍历Excel中的内容构造字典树
Args:
col: 模块所在列
Returns:
成功True, 失败False
'''
# 获取所有sheet页名称
sheet_names = self.__xl.sheet_names()
for sheet_name in sheet_names:
sheet = self.__xl.sheet_by_name(sheet_name)
nrows = sheet.nrows #总行数
# 没有数据直接忽略
if nrows < 2:
continue
tmp_index = (0, 1) # 第一行为标题
module_dic = {}
non_merged = [] # 未合并的行数
rest_merged = [] # 中间空白行引起的剩余模块
length = 1 # 记录遍历到多少行
for rlow, rhigh, clow, chigh in sheet.merged_cells:
# 不是第一列模块,忽略
if chigh != (col + 1):
continue
# 第一行没有标题
if rlow == 0:
tmp_index = (0, 0)
# 如果有单独一行的模块,或者没有合并单元格的模块
if rlow > tmp_index[1]:
non_merged.append((tmp_index[1], rlow))
else: # 合并的单元格
module_name = sheet.cell_value(rlow, clow)
# 空模块名默认是单例
if module_name.strip() == '':
continue
if module_dic.has_key(module_name):
print(u'ERROR[{}-{}]合并单元格中有重复模块名[{}]'.format(self.__filename, sheet_name, module_name))
return False
module_dic[module_name] = {'coord': (rlow, rhigh)}
length = rhigh
tmp_index = (rlow, rhigh)
# 尾部剩余的没有合并单元格的添加进去
if length != nrows:
non_merged.append((length, nrows))
for i in range(0, len(non_merged) - 1):
_, rlow = non_merged[i]
_, rhigh = non_merged[i+1]
rest_merged.append((rlow, rhigh))
# 尾部还是单行的,或者没有合并的
start = 0
end = 1
last_name = ''
for rlow, rhigh in rest_merged:
module_name = sheet.cell_value(rlow, col)
# 如果该单元格有模块名
if module_name.strip() != '':
if module_dic.has_key(module_name):
# 解决读取BUG会把Excel多个合并单元格全部读取出来
low, high = module_dic.get(module_name)['coord']
if rlow != low and rhigh != high:
print('ERROR[{}-{}]有重复模块名[{}]'.format(self.__filename, sheet_name, module_name))
return False
else:
continue
# 记录模块行数坐标
module_dic[module_name] = {'coord': (rlow, rhigh)}
start = rlow
end = rhigh
last_name = module_name
else: # 空白行由于步骤引起的,属于上一个模块
if last_name.strip() == '':
continue
module_dic[last_name] = {'coord': (start, end+1)}
end = end + 1
# 单例模式
if last_name == '' and not module_dic:
module_dic['single_case'] = {'coord': (1, nrows)}
self.__xml_tree[sheet_name] = module_dic
return True
def __get_step(self, sheet_name, rlow, rhigh, col_action, col_result, execution_type):
'''
获取标题对应的步骤和结果
返回step列表
'''
steps = []
step_num = 1
# sheet页实例
sheet = self.__xl.sheet_by_name(sheet_name)
for row in range(rlow, rhigh):
# 动作
action = sheet.cell_value(row, col_action)
# 结果
result = sheet.cell_value(row, col_result)
# 构造字典,键值和节点名称对应
data = {
'step_number': str(step_num),
'actions': self.__auto_break(action),
'expectedresults': self.__auto_break(result),
'execution_type': execution_type
}
step_num = step_num + 1
steps.append(data)
return steps
def __get_title(self, sheet_name, col):
'''
获取sheet中模块对应的所有测试案例标题及起止行数
Args:
sheet_name: sheet页名称
col: 标题所在列数从0计数
Returns:
成功True, 失败False
'''
sheet = self.__xl.sheet_by_name(sheet_name)
for k, v in self.__xml_tree[sheet_name].items():
title_dic = {} # 标题字典
start = 0 # 记录起始行
end = 0 # 记录空白的终止行
curr_title = ''# 当前标题
for row in range(v['coord'][0], v['coord'][1]):
title = sheet.cell_value(row, col)
if title.strip() != '': # 如果是标题行
title_dic[title] = {'coord': (row, row + 1)}
start = row
end = row + 1
curr_title = title
else: # 如果是步骤引发的空格
end = end + 1
title_dic[curr_title] = {'coord': (start, end)}
self.__xml_tree[sheet_name][k]['testcase'] = title_dic
return True
def __auto_break(self, text):
'''
实现步骤和结果自动换行
Args:
text: 待换行的字符床
'''
doc = re.sub(r'\n', r'<br />', text, re.M)
return doc
def create_tree(self):
'''
根据TestLink格式要求构造字典树
Returns:
成功True, 失败False
'''
if not self.__get_modules(0):
print(u'ERROR: 创建字典树中,读取模块失败')
return False
# sheet页模块
for k, v in self.__xml_tree.items():
# print('一级模块{}'.format(k))
# sheet数量统计
self.__result[0] = self.__result[0] + 1
sheet = self.__xl.sheet_by_name(k)
if not self.__get_title(k, 1):
print(u'ERROR: 创建字典树中,读取标题失败')
# sheet页中的模块
for mk, mv in v.items():
# print('\t模块[{}]'.format(mk))
# 模块统计
self.__result[1] = self.__result[1] + 1
testcase = sorted(mv['testcase'].items(), key = lambda x:x[0])
for title, content in testcase:
# print('\t\t标题[{}]'.format(title))
# 测试案例统计
self.__result[2] = self.__result[2] + 1
row = content['coord'][0] # 标题所在行
summary = sheet.cell_value(row, 4) # 摘要
precond = sheet.cell_value(row, 5) # 前提
exe_type = self.__get_exec_type(sheet.cell_value(row, 3)) # 执行方式
importance = self.__get_importance(sheet.cell_value(row, 2)) # 重要性
# 步骤
steps = self.__get_step(k, content['coord'][0], content['coord'][1], 6, 7, exe_type)
case = {
'node_order': str(self.__result[2]),
'version': '1',
'summary': self.__auto_break(summary),
'preconditions': self.__auto_break(precond),
'execution_type': exe_type,
'importance': importance,
'status': '1',
'is_open': '1',
'active': '1',
'steps': steps
}
self.__xml_tree[k][mk]['testcase'][title]['case'] = case
return True
def get_tree(self):
'''
返回构造好的字典树
'''
return self.__xml_tree
def write_xml(self, file_path):
'''
把字典树导出为xml格式
Args:
file_path: xml文件保存路径
Returns:
成功True, 失败False
'''
tree = sorted(self.__xml_tree.items(), key = lambda x:x[0])
for k, v in tree:
# 创建根节点
dom = XmlDom()
root = None
# 如果是单例模式
if v.has_key('single_case'):
root = dom.create_root('testcases')
else:
root = dom.create_root('testsuite')
# k-sheet页名
root.setAttribute('name', k)
# 遍历模块
order = 1
module_v = sorted(v.items(), key = lambda x:x[0])
for mk, mv in module_v:
# 如果不是单例模式,则创建模块节点
module = None
if mk != 'single_case':
module = dom.create_node('testsuite')
# mk-模块名
module.setAttribute('name', mk)
root.appendChild(module)
module_order = dom.create_cdata('node_order', str(order))
module.appendChild(module_order)
order = order + 1
else: # 单例模式模块节点用根节点覆盖
module = root
case_title = sorted(mv['testcase'].items(), key = lambda x:x[0])
for title, cases in case_title:
# 标题案例节点
testcase = dom.create_node('testcase')
testcase.setAttribute('name', title)
module.appendChild(testcase)
for node, text in cases['case'].items():
# 非step节点直接写入
if node != 'steps':
tmp = dom.create_cdata(node, text)
testcase.appendChild(tmp)
else:
steps = dom.create_node(node)
testcase.appendChild(steps)
# steps是一个列表
for step_list in text:
step = dom.create_node('step')
steps.appendChild(step)
# 创建steps中的单个step
for node, text in step_list.items():
tmp = dom.create_cdata(node, text)
step.appendChild(tmp)
# 创建输出文件名
file_name = ''
if v.has_key('single_case'):
file_name = u'Example_Testsuite_Default_%s_Testcases.xml' % k
else:
file_name = u'Example_Testsuite_Default_%s_Testsuite.xml' % k
path = os.path.join(file_path.decode('gb2312'), file_name)
# 写入文件
dom.write(path)
return True
def get_result(self):
'''
返回转换sheet模块和测试案例统计
'''
return self.__result
class XmlDom(object):
"""
创建xml文件方法使用minidom方式创建
"""
def __init__(self):
self.dom = None
def create_root(self, name):
'''
创建根节点
Args:
name: 根节点名称
'''
self.dom = minidom.getDOMImplementation().createDocument(None, name, None)
root = self.dom.documentElement
return root
def create_node(self, name):
'''
创建一个节点
Args:
name: 节点名称
'''
node = self.dom.createElement(name)
return node
def create_cdata(self, name, text=''):
"""
创建CDATA数据节点
Args:
name: 节点名
text: 写入节点的值
Returns:
节点对象
"""
node = self.dom.createElement(name)
cdata_text = self.dom.createCDATASection(text)
node.appendChild(cdata_text)
return node
def create_text(self, name, text=''):
"""
创建普通Text数据节点
Args:
name: 节点名
text: 写入节点的值
Returns:
节点对象
"""
node = self.dom.createElement(name)
cdata_text = self.dom.createTextNode(text)
node.appendChild(cdata_text)
return node
def write(self, file_name):
'''
写入xml文件
Args:
file_name: 写入的文件名
'''
# 先写入
with open(file_name, 'w') as f:
xml_str = self.dom.toprettyxml()
pretty = self.__pretty_xml(xml_str)
f.write(pretty)
# 释放
self.dom.unlink()
def __pretty_xml(self, xml_doc):
'''
格式化xml中的CDATA
'''
doc = re.sub(r'>\n<\!\[', r'><![', xml_doc, 0, re.M)
doc = re.sub(r'\]\]>(\s+)<', r']]><', doc, 0, re.M)
return doc
if __name__ == "__main__":
print(u'Excel转XML助手用于TestLink导入测试案例')

View File

@ -0,0 +1,32 @@
# -*- encoding: utf-8 -*-
import os
import sys
# 用于环境检查
# 检查Python版本
# 检查Python模块需要xlrd
def check_env():
# 检查input文件夹
input_dir = os.path.join(os.getcwd(), 'input')
if not os.path.exists(input_dir):
os.mkdir(input_dir)
# 检查xlrd模块
try:
import xlrd
except:
print(u'缺少xlrd模块将自动安装...')
# 执行安装
p = os.popen('pip install xlrd')
print(p.read())
import xlrd
finally:
print('***************************')
print(u'环境配置正常,可以运行转换工具')
print('***************************')
if __name__ == "__main__":
check_env()

View File

@ -0,0 +1,101 @@
# -*- coding: utf-8 -*-
from ExcelToXml import ExcelToXml
import os
import sys
# 定义后缀过滤列表
filter_list = ['.xls']
# 说明检查output文件夹是否存在不存在则创建
# 参数:无
# 返回值:输出路径
def get_output_dir():
output = os.path.join(os.getcwd(), 'output')
if not os.path.exists(output):
os.mkdir(output)
return output
# 说明遍历指定文件夹扫描Excel文件
# 参数:无
# 返回值:输出文件夹下所有文件路径列表
def get_input_excel(input_dir):
files = []
# input文件夹不存在
if not os.path.exists(input_dir):
return files
else: # 遍历该目录下所有文件
for _, _, file_list in os.walk(input_dir):
for filename in file_list:
file_path = os.path.join(input_dir, filename)
# 获取文件后缀
ext = os.path.splitext(file_path)[1]
# 满足要求的则放入
if ext in filter_list:
files.append(file_path)
return files
# 将命令行参数转换为对应的文件输入
def get_command():
excels = []
input_dir = ''
# 未传入参数,使用默认设置
if len(sys.argv) == 1:
input_dir = os.path.join(os.getcwd(), 'input')
excels = get_input_excel(input_dir)
elif len(sys.argv) == 2: # 传入了参数
input_dir = sys.argv[1]
# 如果传入的是单个文件
if os.path.isfile(input_dir):
ext = os.path.splitext(input_dir)[1]
# 判断是否是Excel
if ext not in filter_list:
print(u'ERROR请传入正确的Excel文件')
return False
excels.append(input_dir)
elif os.path.exists(input_dir): # 如果是文件夹
excels = get_input_excel(input_dir)
return excels, input_dir
# 说明将指定Excel转换为TestLink使用的xml文件
# 参数:无
# 返回值成功返回True
def converter():
# 获取命令行参数
excels, input_dir = get_command()
# 校验文件输入
if len(excels) == 0:
print(u'ERROR: 未发现输入的Excel文件')
return False
print('--------------------------------')
print u'INFO: 当前输入文件为:%s' % input_dir.decode('gb2312')
output = get_output_dir()
# 遍历文件输出
print(u'INFO: 开始执行Excel转xml...')
print('--------------------------------')
for excel in excels:
# 文件名
_, filename = os.path.split(excel)
# 实例化对象
xm = ExcelToXml(excel)
if not xm.create_tree():
print 'ERROR: [%s]转换发生错误' % filename.decode('gb2312')
print('------------------------')
continue
# 写入xml文件
xm.write_xml(output)
result = xm.get_result()
print u'[%s]转换成功!' % filename.decode('gb2312')
print u'Sheet总数为\t%s' % result[0]
print u'模块总数为:\t%s' % result[1]
print u'案例总数为:\t%s' % result[2]
print('------------------------')
print u'INFO: 全部转换成功,已输出到文件夹:%s' % output.decode('gb2312')
return True
if __name__ == "__main__":
# 开始转换
converter()

View File

@ -0,0 +1,20 @@
环境要求:
1、Python 2.x
2、依赖模块xlrd
可以使用 python check.py 命令完成版本检查和模块安装
命令行参数:
1、python converter.py
所有Excel文件放入input文件夹下输出的xml文件存放在output文件夹下自动创建
2、python converter.py 文件夹路径
指定输入文件夹文件夹路径名不能有空格输出的xml文件存放在output文件夹下
3、python converter.py Excel文件路径
指定输入文件输出的xml文件存放在output文件夹下
模板要求不支持xlsx格式的Excel
1、目录结构模式
输出的xml结构会按照【sheet页名】->【模块名】->【标题】导入TestLink如果TestLink没有对应目录会自动创建。
2、用例集模式
Excel中模块列为空白生成的xml测试用例集用于导入单个目录下

View File

@ -0,0 +1,412 @@
# -*- coding:utf-8 -*-
# 日期2019-8-14
# 作者Wang WenJie
# 版本1.2.2
# 说明Excel转Xml用于TestLink导入测试案例
#*******************************************
# 修改说明:
# 2019-9-10修复CDATA类型文件转换后无法被读取
# 修改换行标记
# 2019-10-22修复模块中间出现空白行解析不全
#*******************************************
import xlrd
import xml.dom.minidom as minidom
import re
import os
class ExcelToXml(object):
"""
Excel转Xml用于TestLink导入测试案例
构造字典树遍历写入xml文件
"""
def __init__(self, file_name):
try:
self.__xl = xlrd.open_workbook(file_name, formatting_info = True)
except NotImplementedError:
raise ValueError('不支持xlsx格式文件')
self.__xml_tree = {} # 全局字典树
self.__result = [0, 0, 0] #sheet页模块测试案例
_, self.__filename = os.path.split(file_name)
def __get_importance(self, zh_str):
'''
重要性中文转对应数字不存在返回原字符
Args:
zh_str: 待转换字符
'''
if zh_str == '':
return '2'
elif zh_str == '':
return '1'
elif zh_str == '':
return '3'
# 都不存在返回原字符
return zh_str
def __get_exec_type(self, zh_str):
'''
执行方式中文转对应数字不存在返回原字符
Args:
zh_str: 待转换字符
'''
if zh_str == '手工':
return '1'
elif zh_str == '自动的':
return '2'
# 都不存在返回原字符
return zh_str
def __get_modules(self, col):
'''
从各个sheet页中的模块名开始遍历Excel中的内容构造字典树
Args:
col: 模块所在列
Returns:
成功True, 失败False
'''
# 获取所有sheet页名称
sheet_names = self.__xl.sheet_names()
for sheet_name in sheet_names:
sheet = self.__xl.sheet_by_name(sheet_name)
nrows = sheet.nrows #总行数
# 没有数据直接忽略
if nrows < 2:
continue
tmp_index = (0, 1) # 第一行为标题
module_dic = {}
non_merged = [] # 未合并的行数
rest_merged = [] # 中间空白行引起的剩余模块
length = 1 # 记录遍历到多少行
for rlow, rhigh, clow, chigh in sheet.merged_cells:
# 不是第一列模块,忽略
if chigh != (col + 1):
continue
# 第一行没有标题
if rlow == 0:
tmp_index = (0, 0)
# 如果有单独一行的模块,或者没有合并单元格的模块
if rlow > tmp_index[1]:
non_merged.append((tmp_index[1], rlow))
elif rlow == tmp_index[1]: # 合并的单元格
module_name = sheet.cell_value(rlow, clow)
# 空模块名默认是单例
if module_name.strip() == '':
continue
if module_dic.__contains__(module_name):
print('ERROR[{}-{}]合并单元格中有重复模块名[{}]'.format(self.__filename, sheet_name, module_name))
return False
module_dic[module_name] = {'coord': (rlow, rhigh)}
length = rhigh
tmp_index = (rlow, rhigh)
# 尾部剩余的没有合并单元格的添加进去
if length != nrows:
non_merged.append((length, nrows))
for i in range(0, len(non_merged) - 1):
_, rlow = non_merged[i]
_, rhigh = non_merged[i+1]
rest_merged.append((rlow, rhigh))
# 尾部还是单行的,或者没有合并的
start = 0
end = 1
last_name = ''
for rlow, rhigh in rest_merged:
module_name = sheet.cell_value(rlow, col)
# 如果该单元格有模块名
if module_name.strip() != '':
if module_dic.__contains__(module_name):
# 解决读取BUG会把Excel多个合并单元格全部读取出来
low, high = module_dic.get(module_name)['coord']
if rlow != low and rhigh != high:
print('ERROR[{}-{}]有重复模块名[{}]'.format(self.__filename, sheet_name, module_name))
return False
else:
continue
# 记录模块行数坐标
module_dic[module_name] = {'coord': (rlow, rhigh)}
start = rlow
end = rhigh
last_name = module_name
else: # 空白行由于步骤引起的,属于上一个模块
if last_name.strip() == '':
continue
module_dic[last_name] = {'coord': (start, end+1)}
end = end + 1
# 单例模式
if last_name == '' and not module_dic:
module_dic['single_case'] = {'coord': (1, nrows)}
self.__xml_tree[sheet_name] = module_dic
return True
def __get_step(self, sheet_name, rlow, rhigh, col_action, col_result, execution_type):
'''
获取标题对应的步骤和结果
返回step列表
'''
steps = []
step_num = 1
# sheet页实例
sheet = self.__xl.sheet_by_name(sheet_name)
for row in range(rlow, rhigh):
# 动作
action = sheet.cell_value(row, col_action)
# 结果
result = sheet.cell_value(row, col_result)
# 构造字典,键值和节点名称对应
data = {
'step_number': str(step_num),
'actions': self.__auto_break(action),
'expectedresults': self.__auto_break(result),
'execution_type': execution_type
}
step_num = step_num + 1
steps.append(data)
return steps
def __get_title(self, sheet_name, col):
'''
获取sheet中模块对应的所有测试案例标题及起止行数
Args:
sheet_name: sheet页名称
col: 标题所在列数从0计数
Returns:
成功True, 失败False
'''
sheet = self.__xl.sheet_by_name(sheet_name)
for k, v in self.__xml_tree[sheet_name].items():
title_dic = {} # 标题字典
start = 0 # 记录起始行
end = 0 # 记录空白的终止行
curr_title = ''# 当前标题
for row in range(v['coord'][0], v['coord'][1]):
title = sheet.cell_value(row, col)
if title.strip() != '': # 如果是标题行
title_dic[title] = {'coord': (row, row + 1)}
start = row
end = row + 1
curr_title = title
else: # 如果是步骤引发的空格
end = end + 1
title_dic[curr_title] = {'coord': (start, end)}
self.__xml_tree[sheet_name][k]['testcase'] = title_dic
return True
def __auto_break(self, text):
'''
实现步骤和结果自动换行
Args:
text: 待换行的字符床
'''
doc = re.sub(r'\n', r'<br />', text, re.M)
return doc
def create_tree(self):
'''
根据TestLink格式要求构造字典树
Returns:
成功True, 失败False
'''
if not self.__get_modules(0):
print('ERROR: 创建字典树中,读取模块失败')
return False
# sheet页模块
for k, v in self.__xml_tree.items():
# print('一级模块{}'.format(k))
# sheet数量统计
self.__result[0] = self.__result[0] + 1
sheet = self.__xl.sheet_by_name(k)
if not self.__get_title(k, 1):
print('ERROR: 创建字典树中,读取标题失败')
# sheet页中的模块
for mk, mv in v.items():
# print('\t模块[{}]'.format(mk))
# 模块统计
self.__result[1] = self.__result[1] + 1
for title, content in mv['testcase'].items():
# print('\t\t标题[{}]'.format(title))
# 测试案例统计
self.__result[2] = self.__result[2] + 1
row = content['coord'][0] # 标题所在行
summary = sheet.cell_value(row, 4) # 摘要
precond = sheet.cell_value(row, 5) # 前提
exe_type = self.__get_exec_type(sheet.cell_value(row, 3)) # 执行方式
importance = self.__get_importance(sheet.cell_value(row, 2)) # 重要性
# 步骤
steps = self.__get_step(k, content['coord'][0], content['coord'][1], 6, 7, exe_type)
case = {
'node_order': str(self.__result[2]),
'version': '1',
'summary': self.__auto_break(summary),
'preconditions': self.__auto_break(precond),
'execution_type': exe_type,
'importance': importance,
'status': '1',
'is_open': '1',
'active': '1',
'steps': steps
}
self.__xml_tree[k][mk]['testcase'][title]['case'] = case
return True
def get_tree(self):
'''
返回构造好的字典树
'''
return self.__xml_tree
def write_xml(self, file_path):
'''
把字典树导出为xml格式
Args:
file_path: xml文件保存路径
Returns:
成功True, 失败False
'''
for k, v in self.__xml_tree.items():
# 创建根节点
dom = XmlDom()
root = None
# 如果是单例模式
if v.__contains__('single_case'):
root = dom.create_root('testcases')
else:
root = dom.create_root('testsuite')
# k-sheet页名
root.setAttribute('name', k)
# 遍历模块
order = 1
for mk, mv in v.items():
# 如果不是单例模式,则创建模块节点
module = None
if mk != 'single_case':
module = dom.create_node('testsuite')
# mk-模块名
module.setAttribute('name', mk)
root.appendChild(module)
module_order = dom.create_cdata('node_order', str(order))
module.appendChild(module_order)
order = order + 1
else: # 单例模式模块节点用根节点覆盖
module = root
for title, cases in mv['testcase'].items():
# 标题案例节点
testcase = dom.create_node('testcase')
testcase.setAttribute('name', title)
module.appendChild(testcase)
for node, text in cases['case'].items():
# 非step节点直接写入
if node != 'steps':
tmp = dom.create_cdata(node, text)
testcase.appendChild(tmp)
else:
steps = dom.create_node(node)
testcase.appendChild(steps)
# steps是一个列表
for step_list in text:
step = dom.create_node('step')
steps.appendChild(step)
# 创建steps中的单个step
for node, text in step_list.items():
tmp = dom.create_cdata(node, text)
step.appendChild(tmp)
# 创建输出文件名
file_name = ''
if v.__contains__('single_case'):
file_name = r'Example_Testsuite_Default_{}_Testcases.xml'.format(k)
else:
file_name = r'Example_Testsuite_Default_{}_Testsuite.xml'.format(k)
path = os.path.join(file_path, file_name)
# 写入文件
dom.write(path)
return True
def get_result(self):
'''
返回转换sheet模块和测试案例统计
'''
return self.__result
class XmlDom(object):
"""
创建xml文件方法使用minidom方式创建
"""
def __init__(self):
self.dom = None
def create_root(self, name):
'''
创建根节点
Args:
name: 根节点名称
'''
self.dom = minidom.getDOMImplementation().createDocument(None, name, None)
root = self.dom.documentElement
return root
def create_node(self, name):
'''
创建一个节点
Args:
name: 节点名称
'''
node = self.dom.createElement(name)
return node
def create_cdata(self, name, text=''):
"""
创建CDATA数据节点
Args:
name: 节点名
text: 写入节点的值
Returns:
节点对象
"""
node = self.dom.createElement(name)
cdata_text = self.dom.createCDATASection(text)
node.appendChild(cdata_text)
return node
def write(self, file_name):
'''
写入xml文件
Args:
file_name: 写入的文件名
'''
# 先写入
with open(file_name, 'w', encoding='utf-8') as f:
xml_str = self.dom.toprettyxml()
pretty = self.__pretty_xml(xml_str)
f.write(pretty)
# 释放
self.dom.unlink()
def __pretty_xml(self, xml_doc):
'''
格式化xml中的CDATA
'''
doc = re.sub(r'>\n<\!\[', r'><![', xml_doc, 0, re.M)
doc = re.sub(r'\]\]>(\s+)<', r']]><', doc, 0, re.M)
return doc
if __name__ == "__main__":
print('Excel转XML助手用于TestLink导入测试案例')

View File

@ -0,0 +1,37 @@
import os
import sys
# 用于环境检查
# 检查Python版本
# 检查Python模块需要xlrd
def check_env():
# 检查Python版本
if sys.version_info < (3, 0):
print('***************************')
print('ERROR: Python版本低于3.0')
print('***************************')
return
# 检查input文件夹
input_dir = os.path.join(os.getcwd(), 'input')
if not os.path.exists(input_dir):
os.mkdir(input_dir)
# 检查xlrd模块
try:
import xlrd
except:
print('缺少xlrd模块将自动安装...')
# 执行安装
p = os.popen('pip install xlrd')
print(p.read())
import xlrd
finally:
print('***************************')
print('环境配置正常,可以运行转换工具')
print('***************************')
if __name__ == "__main__":
check_env()

View File

@ -0,0 +1,100 @@
# -*- coding: utf-8 -*-
from ExcelToXml import ExcelToXml
import sys
import os
# 定义后缀过滤列表
filter_list = ['.xls']
# 说明检查output文件夹是否存在不存在则创建
# 参数:无
# 返回值:输出路径
def get_output_dir():
output = os.path.join(os.getcwd(), 'output')
if not os.path.exists(output):
os.mkdir(output)
return output
# 说明遍历指定文件夹扫描Excel文件
# 参数:无
# 返回值:输出文件夹下所有文件路径列表
def get_input_excel(input_dir):
files = []
# input文件夹不存在
if not os.path.exists(input_dir):
return files
else: # 遍历该目录下所有文件
for _, _, file_list in os.walk(input_dir):
for filename in file_list:
file_path = os.path.join(input_dir, filename)
# 获取文件后缀
ext = os.path.splitext(file_path)[1]
# 满足要求的则放入
if ext in filter_list:
files.append(file_path)
return files
# 将命令行参数转换为对应的文件输入
def get_command():
excels = []
input_dir = ''
# 未传入参数,使用默认设置
if len(sys.argv) == 1:
input_dir = os.path.join(os.getcwd(), 'input')
excels = get_input_excel(input_dir)
elif len(sys.argv) == 2: # 传入了参数
input_dir = sys.argv[1]
# 如果传入的是单个文件
if os.path.isfile(input_dir):
ext = os.path.splitext(input_dir)[1]
# 判断是否是Excel
if ext not in filter_list:
print('ERROR请传入正确的Excel文件')
return False
excels.append(input_dir)
elif os.path.exists(input_dir): # 如果是文件夹
excels = get_input_excel(input_dir)
return excels, input_dir
# 说明将指定Excel转换为TestLink使用的xml文件
# 参数:无
# 返回值成功返回True
def converter():
# 获取命令行参数
excels, input_dir = get_command()
# 校验文件输入
if len(excels) == 0:
print('ERROR: 未发现输入的Excel文件')
return False
print('--------------------------------')
print('INFO: 当前输入文件为:', input_dir)
output = get_output_dir()
# 遍历文件输出
print('INFO: 开始执行Excel转xml...')
print('--------------------------------')
for excel in excels:
# 文件名
_, filename = os.path.split(excel)
# 实例化对象
xm = ExcelToXml(excel)
if not xm.create_tree():
print(u'ERROR: [{}]转换发生错误'.format(filename))
print('------------------------')
continue
# 写入xml文件
xm.write_xml(output)
result = xm.get_result()
print('[{}]转换成功!'.format(filename))
print('Sheet总数为\t', result[0])
print('模块总数为:\t', result[1])
print('案例总数为:\t', result[2])
print('------------------------')
print('INFO: 全部转换成功,已输出到文件夹:', output)
return True
if __name__ == "__main__":
# 开始转换
converter()

View File

@ -0,0 +1,20 @@
环境要求:
1、Python 3.x
2、依赖模块xlrd
可以使用 python check.py 命令完成版本检查和模块安装
命令行参数:
1、python converter.py
所有Excel文件放入input文件夹下输出的xml文件存放在output文件夹下自动创建
2、python converter.py 文件夹路径
指定输入文件夹文件夹路径名不能有空格输出的xml文件存放在output文件夹下
3、python converter.py Excel文件路径
指定输入文件输出的xml文件存放在output文件夹下
模板要求不支持xlsx格式的Excel
1、目录结构模式
输出的xml结构会按照【sheet页名】->【模块名】->【标题】导入TestLink如果TestLink没有对应目录会自动创建。
2、用例集模式
Excel中模块列为空白生成的xml测试用例集用于导入单个目录下

View File

@ -0,0 +1,285 @@
# -*- 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
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 u''
elif num == '2':
return u''
elif num == '3':
return u''
return num
def __get_exec_type(self, num):
'''
执行方式序号转换成对应的中文
'''
if num == '1':
return u'手工'
elif num == '2':
return u'自动的'
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 = []
# 处理二级模块情况
if len(root.getElementsByTagName('testsuite')) == 0:
suites.append(self._read_module(root))
return suites
for testsuite in root.getElementsByTagName('testsuite'):
suites.append(self._read_module(testsuite))
return suites
def _read_module(self, parent):
module_tree = {}
# 模块名
module = parent.getAttribute('name')
module_tree['module'] = module
module_tree['testcase'] = []
module_row = 0
for case in parent.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
return module_tree
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("The root name of xml cann't be null. Please check the name attruite of testsuite in line 1 or 2.");
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 = u'宋体'
# 对齐方式
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 = ('<p>', '</p>', '&nbsp;')
for s in symbol:
text = re.sub(s, '', text)
# 替换为换行
text = re.sub(r'<.+[/]{1}>|<[/]{1}.>|<br>', 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 = ((u'模块', 35), (u'用例标题', 50), (u'重要性', 25), (u'执行方式', 27), (u'摘要', 80), (u'前提', 80), (u'步骤', 160), (u'结果', 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__":
print(u'xml转Excel助手用于TestLink导出测试案例转换')

View File

@ -0,0 +1,39 @@
# *-* coding: utf-8 *-*
import os
import sys
# 用于环境检查
# 检查Python版本
# 检查Python模块需要xlrd
def check_env():
# 检查Python版本
print(sys.version_info)
if sys.version_info > (2, 9):
print('***************************')
print('ERROR: Python版本高于2.x')
print('***************************')
return
# 检查input文件夹
input_dir = os.path.join(os.getcwd(), 'input')
if not os.path.exists(input_dir):
os.mkdir(input_dir)
# 检查xlrd模块
try:
import xlwt
except:
print('缺少xlwt模块将自动安装...')
# 执行安装
p = os.popen('pip install xlwt')
print(p.read())
import xlrd
finally:
print('***************************')
print('环境配置正常,可以运行转换工具')
print('***************************')
if __name__ == "__main__":
check_env()

View File

@ -0,0 +1,98 @@
# -*- coding: utf-8 -*-
from XmlToExcel import XmlToExcel
import sys
import os
import time
# 定义后缀过滤列表
filter_list = ['.xml']
# 说明检查output文件夹是否存在不存在则创建
# 参数:无
# 返回值:输出路径
def get_output_dir():
output = os.path.join(os.getcwd(), 'output')
if not os.path.exists(output):
os.mkdir(output)
return output
# 说明遍历指定文件夹扫描xml文件
# 参数:无
# 返回值输出文件夹下所有xml文件路径列表
def get_input_xml(input_dir):
files = []
# input文件夹不存在
if not os.path.exists(input_dir):
return files
else: # 遍历该目录下所有文件
for _, _, file_list in os.walk(input_dir):
for filename in file_list:
file_path = os.path.join(input_dir, filename)
# 获取文件后缀
ext = os.path.splitext(file_path)[1]
# 满足要求的则放入
if ext in filter_list:
files.append(file_path)
return files
# 将命令行参数转换为对应的文件输入
def get_command():
xmls = []
input_dir = ''
# 未传入参数,使用默认设置
if len(sys.argv) == 1:
input_dir = os.path.join(os.getcwd(), 'input')
xmls = get_input_xml(input_dir)
elif len(sys.argv) == 2: # 传入了参数
input_dir = sys.argv[1]
# 如果传入的是单个文件
if os.path.isfile(input_dir):
ext = os.path.splitext(input_dir)[1]
# 判断是否是xml
if ext not in filter_list:
print(u'ERROR请传入正确的xml文件')
return False
xmls.append(input_dir)
elif os.path.exists(input_dir): # 如果是文件夹
xmls = get_input_xml(input_dir)
return xmls, input_dir
# 说明将指定xml转换为Excel文件
# 参数:无
# 返回值成功返回True
def converter():
# 获取命令行参数
xmls, input_dir = get_command()
# 校验文件输入
if len(xmls) == 0:
print(u'ERROR: 未发现输入的xml文件')
return False
print('--------------------------------')
# print(u'INFO: 当前输入文件为:{}'.format(input_dir))
output = get_output_dir()
# 遍历文件输出
print(u'INFO: 开始执行xml转Excel...')
print('--------------------------------')
# 实例化
xm = XmlToExcel()
# 返回转换结果
result = xm.read_xml(xmls)
file_name = time.strftime(r'%Y%m%d%H%M%S', time.localtime()) + '_Example_Testsuite_Default.xls'
excel = os.path.join(output, file_name)
# 写入Excel文件
res = xm.write_excel(excel)
print(u'成功转换文件数:\t{}'.format(result['success']))
print(u'失败转换文件数:\t{}'.format(result['fail']))
print(u'模块总数为:\t{}'.format(res['module']))
print(u'案例总数为:\t{}'.format(res['case']))
print('------------------------')
print(u'INFO: 全部转换成功,已输出到文件:{}'.format(excel))
return True
if __name__ == "__main__":
# 开始转换
converter()

View File

@ -0,0 +1,16 @@
环境要求:
1、Python 2.x
2、依赖模块xlwt
可以使用 python check.py 命令完成版本检查和模块安装
命令行参数:
1、python converter.py
所有Xml文件放入input文件夹下输出的Excel文件存放在output文件夹下自动创建
2、python converter.py 文件夹路径
指定输入文件夹文件夹路径名不能有空格输出的xml文件存放在output文件夹下
3、python converter.py xml文件路径
指定输入文件输出的Excel文件存放在output文件夹下
导出的文件命名规则为:
时间戳+_Example_Testsuite_Default.xls

View File

@ -0,0 +1,282 @@
# -*- 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 = ('<p>', '</p>', '&nbsp;')
for s in symbol:
text = re.sub(s, '', text, flags=re.M|re.I|re.S)
# 替换为换行
text = re.sub(r'<.+[/]{1}>|<[/]{1}.>|<br>', 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)

View File

@ -0,0 +1,38 @@
# *-* coding: utf-8 *-*
import os
import sys
# 用于环境检查
# 检查Python版本
# 检查Python模块需要xlrd
def check_env():
# 检查Python版本
if sys.version_info < (3, 0):
print('***************************')
print('ERROR: Python版本低于3.0')
print('***************************')
return
# 检查input文件夹
input_dir = os.path.join(os.getcwd(), 'input')
if not os.path.exists(input_dir):
os.mkdir(input_dir)
# 检查xlrd模块
try:
import xlwt
except:
print('缺少xlwt模块将自动安装...')
# 执行安装
p = os.popen('pip install xlwt')
print(p.read())
import xlrd
finally:
print('***************************')
print('环境配置正常,可以运行转换工具')
print('***************************')
if __name__ == "__main__":
check_env()

View File

@ -0,0 +1,98 @@
# -*- coding: utf-8 -*-
from XmlToExcel import XmlToExcel
import sys
import os
import time
# 定义后缀过滤列表
filter_list = ['.xml']
# 说明检查output文件夹是否存在不存在则创建
# 参数:无
# 返回值:输出路径
def get_output_dir():
output = os.path.join(os.getcwd(), 'output')
if not os.path.exists(output):
os.mkdir(output)
return output
# 说明遍历指定文件夹扫描xml文件
# 参数:无
# 返回值输出文件夹下所有xml文件路径列表
def get_input_xml(input_dir):
files = []
# input文件夹不存在
if not os.path.exists(input_dir):
return files
else: # 遍历该目录下所有文件
for _, _, file_list in os.walk(input_dir):
for filename in file_list:
file_path = os.path.join(input_dir, filename)
# 获取文件后缀
ext = os.path.splitext(file_path)[1]
# 满足要求的则放入
if ext in filter_list:
files.append(file_path)
return files
# 将命令行参数转换为对应的文件输入
def get_command():
xmls = []
input_dir = ''
# 未传入参数,使用默认设置
if len(sys.argv) == 1:
input_dir = os.path.join(os.getcwd(), 'input')
xmls = get_input_xml(input_dir)
elif len(sys.argv) == 2: # 传入了参数
input_dir = sys.argv[1]
# 如果传入的是单个文件
if os.path.isfile(input_dir):
ext = os.path.splitext(input_dir)[1]
# 判断是否是xml
if ext not in filter_list:
print(u'ERROR请传入正确的xml文件')
return False
xmls.append(input_dir)
elif os.path.exists(input_dir): # 如果是文件夹
xmls = get_input_xml(input_dir)
return xmls, input_dir
# 说明将指定xml转换为Excel文件
# 参数:无
# 返回值成功返回True
def converter():
# 获取命令行参数
xmls, input_dir = get_command()
# 校验文件输入
if len(xmls) == 0:
print('ERROR: 未发现输入的xml文件')
return False
print('--------------------------------')
print('INFO: 当前输入文件为:', input_dir)
output = get_output_dir()
# 遍历文件输出
print('INFO: 开始执行xml转Excel...')
print('--------------------------------')
# 实例化
xm = XmlToExcel()
# 返回转换结果
result = xm.read_xml(xmls)
file_name = time.strftime(r'%Y%m%d%H%M%S', time.localtime()) + '_Example_Testsuite_Default.xls'
excel = os.path.join(output, file_name)
# 写入Excel文件
res = xm.write_excel(excel)
print('成功转换文件数:\t', result['success'])
print('失败转换文件数:\t', result['fail'])
print('模块总数为:\t', res['module'])
print('案例总数为:\t', res['case'])
print('------------------------')
print('INFO: 全部转换成功,已输出到文件:', excel)
return True
if __name__ == "__main__":
# 开始转换
converter()

View File

@ -0,0 +1,16 @@
环境要求:
1、Python 3.x
2、依赖模块xlwt
可以使用 python check.py 命令完成版本检查和模块安装
命令行参数:
1、python converter.py
所有Xml文件放入input文件夹下输出的Excel文件存放在output文件夹下自动创建
2、python converter.py 文件夹路径
指定输入文件夹文件夹路径名不能有空格输出的xml文件存放在output文件夹下
3、python converter.py xml文件路径
指定输入文件输出的Excel文件存放在output文件夹下
导出的文件命名规则为:
时间戳+_Example_Testsuite_Default.xls