提交testlink转换工具
This commit is contained in:
parent
e50111aaad
commit
c9605ad3b5
|
|
@ -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导入测试案例')
|
||||
|
|
@ -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()
|
||||
|
||||
|
|
@ -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()
|
||||
|
|
@ -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测试用例集用于导入单个目录下
|
||||
|
|
@ -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导入测试案例')
|
||||
|
|
@ -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()
|
||||
|
||||
|
|
@ -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()
|
||||
|
|
@ -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测试用例集用于导入单个目录下
|
||||
|
|
@ -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>', ' ')
|
||||
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导出测试案例转换')
|
||||
|
|
@ -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()
|
||||
|
||||
|
|
@ -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()
|
||||
|
|
@ -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
|
||||
|
|
@ -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>', ' ')
|
||||
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)
|
||||
|
|
@ -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()
|
||||
|
||||
|
|
@ -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()
|
||||
|
|
@ -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
|
||||
Loading…
Reference in New Issue