import time
from enum import Enum
# 工具函数
def getlocaltime():
print(time.strftime('%Y-%m-%d %H:%M:%S', time.localtime(time.time())))
def _get_intent(self,indent):
return "+" * ( indent *2 )
def _get_dot_line(self,line,line_max):
if len(line)>(line_max):
return line
else:
return "-"*(line_max - len(line))
# 定义节点类型
class NodeType(Enum):
Step = 1
Case = 2
TestList = 4
Other = 256
# 节点状态
class StepResult(Enum):
INFO =1
PASS =2
FAIL = 4
EXCEPTION = 8
WARNING = 16
ERROR = 32
# 实现测试节点类
class ResultNode:
def __init__(self,header,
status=None ,
msg = "",
parent = None ,
node_type = NodeType.Other):
self.status = StepResult.INFO if status is None else status
self.header = header
self.msg = msg
self.children =list()
self.parent = parent
self.type = node_type
self.timestamp = time.strftime('%Y-%m-%d %H:%M:%S',time.localtime(time.time()))
def add_child(self,header,
status=None ,
msg = "",
parent = None ,
node_type = NodeType.Other):
"""添加新的子节点,并返回该节点"""
new_node = ResultNode(header,
msg = msg,
parent = self,
node_type = node_type) # 实例化一个节点对象
# 节点类型过滤,类型只能为step
if self.type in[NodeType.Step,NodeType.Case]:
new_node.type = NodeType.Step
self.children.append(new_node)
# for i in self.children:
# print("testlog-",i.msg)
new_node.set_status(status)
return new_node
def set_status(self,status):
"""设置当前节点的状态,同时更新父节点的状态"""
# 【设计逻辑】
# 对于以下节点不做父节点进一步设置:
# ---1、非case节点
# ---2、step节点
if self.type == NodeType.Other or status == StepResult.INFO:
return
# 只有以下节点更新当前状态
# ---状态为PASS
if self.status == StepResult.PASS:
self.status = status
# self.parent.set_status(status)
def is_leaf(self):
return any(self.children)
def to_dict(self):
"""将当前节点实例结果生成字典类型的结构"""
rv = dict()
rv["header"] = self.header
rv["status"] = self.status.value
rv["msg"] = self.msg
rv["type"] = self.type #?
rv["timstamp"] = self.timestamp
rv["children"] = list()
# 遍历当前节点实例,将当前节点的子节点信息值存入字典的children键中
for child in self.children:
# print("result",child.to_dict())
rv["children"].append(child.to_dict())
return rv
def to_excel(self):
"""将结果生成excel类型的结构"""
pass
def to_text(self,indent = 0):
"""将结果生成文本类型的的结构"""
pass
def get_test_case_stats(self):
stats_pass = 0
stats_fail = 0
stats_error = 0
stats_exception = 0
if self.type == NodeType.Case:
if self.status == StepResult.PASS:
stats_pass = 1
elif self.status == StepResult.FAIL:
stats_fail = 1
elif self.status == StepResult.EXCEPTION:
stats_exception =1
elif self.status == StepResult.WARNING:
stats_
class ResultReporter:
def __init__(self):
"""
【root】 :根节点
【recent_node】 :保存当前操作的节点,在ResultReporter类初始化的过程中,【recent_node】被赋值为【root】
"""
self.root = ResultNode("Root")
self.recent_node = self.root # recent_node:保存当前操作的节点,每次执行完函数add_node()后,将新的节点赋给recent_node
self.recent_case = None # recent_test:用来存放测试用例
self.recent_list = None # recent_list:用来存放测试列表
# ------------------------------------------------------------------------------------------
# node节点管理
def add_node(self,header,msg="", status=StepResult.INFO, node_type=NodeType.Other):
"""
【add_node()】 :通过调用当前节点的add_child方法来添加新的节点,并且将新建的节点赋值给recent_node
【pop()】
"""
self.recent_node = self.recent_node.add_child(header = header,
status = status,
msg = msg,
node_type = node_type)
def end_node(self):
# 如果存在父节点:那么当前节点recent_node的值 ==>退回设置至当前节点的父节点
if self.recent_node.parent:
self.recent_node = self.recent_node.parent
# ------------------------------------------------------------------------------------------
#测试列表层(TestList)
def add_list(self,list_name):
"""添加测试列表"""
self.recent_node.add_child(msg = "新增测试列表",header=list_name, node_type=NodeType.TestList)
self.recent_list = self.recent_node
def end_list(self):
"""退出当前测试列表,end_list()用于保证当前操作节点能最终回到正确的位置"""
if self.recent_list is None:
return
self.recent_node = self.recent_list.parent # 测试列表发生异常,更新当前操作节点到父节点
self.recent_list = None
# ------------------------------------------------------------------------------------------
# 测试用例层(Case)
def add_test(self,case_name):
"""添加测试用例"""
self.recent_node.add_child(msg = "新增测试用例",header = case_name ,node_type=NodeType.Case)
self.recent_case =self.recent_node
def end_test(self):
"""测试步骤发生异常时使用,end_ test()用于保证当前操作节点能最终回到正确的位置"""
if self.recent_case is None:
return
self.recent_node = self.recent_case.parent # 测试用例发生异常,更新当前操作节点到父节点
self.recent_case = None
# ------------------------------------------------------------------------------------------
# 测试步骤组层(step)
def add_step_group(self,group_name):
"""添加测试步骤组"""
self.recent_node = self.recent_node.add_child(msg = "新增测试步骤组",header = group_name ,node_type=NodeType.Step)
self.recent_step_group = self.recent_node
def end_step_group(self):
self.end_node()
def add(self,status:StepResult,headline,msg =""):
"""用来添加新的叶节点"""
self.recent_node.add_child(header=headline,
msg = msg,
node_type=NodeType.Step,
status=status)
# RN = ResultNode(1,None ,msg="测试顶层节点NODE1",parent = None ,node_type=1)
# RN.add_child(2,status=2 ,msg="测试N+1层节点1" ,parent="NODE1" ,node_type=2)
# RN.add_child(3,status=2 ,msg="测试N+1层节点2" ,parent="NODE1" ,node_type=2)
# RN.set_status(StepResult.PASS)
# print("Rn节点的所有节点信息(顶层节点和子节点):")
# print(RN.to_dict())
OBJ = ResultReporter()
OBJ.add_list("测试列表1")
OBJ.add_test("测试用例1:登录软件")
OBJ.add_step_group("启动")
OBJ.add(StepResult.PASS,"点击电源键","已完成")
OBJ.end_step_group()
OBJ.add_step_group("登录账户")
OBJ.add(StepResult.PASS,"输入用户usr111","已完成")
OBJ.end_step_group()
OBJ.add_step_group("输入密码")
OBJ.add(StepResult.PASS,"输入密码123","已完成")
OBJ.end_step_group()
# OBJ.end_test()
OBJ.add_test("测试用例2:退出软件")
OBJ.add_step_group("点击退出")
OBJ.add(StepResult.PASS,"点击退出按键","已完成")
OBJ.end_step_group()
OBJ.add_step_group("确认退出")
OBJ.add(StepResult.PASS,"点击是","已完成")
OBJ.end_step_group()
OBJ.end_test()
print(OBJ.root.to_dict())