这是实际项目中遇到的一个需求,现在将实现过程简单记录下来,作为备忘。
需求 在项目中开发了审批流程维护工具,开发人员可以使用工具定义审批流程图,现在需要对定义的流程图生成模拟测试用例。流程图有若干分支,针对每一分支,生成一个测试用例,需要遍历所有分支,生成一组测试用例。
数据结构 将流程图看作由节点和连接组成,节点抽象为Node,节点的下游节点用集合 SubNodes表示。为了记录流程节点的执行顺序,创建一个新的结构NodeWithIdx,这个结构包括节点Node,和该节点在当前用例中的执行顺序idx。一组NodeWithIdx构成一个用例。
算法: 创建保存NodeWithIdx的堆栈,从根节点开始, 1、创建NodeWithIdx实例,包含相关的Node实例,并设置Idx=0,将对象压入堆栈 2、判断当前节点是否有子节点,如果没有子节点,那么,堆栈中的节点构成一条路径,记录这条路径,栈顶的对象出栈 3、如果有子节点,但Idx得值已经超过子节点得个数(遍历完成),栈顶的对象出栈 4、按照当前节点(栈顶对象)Idx的值,获取子节点,然后Idx++,如果有子节点,重复第一步创建相应子节点的NodeWithIdx实例,重复第2步,获取子节点 5、如果栈为空,算法结束。否则,栈顶对象的Idx值加1,继续第2步。
代码如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 /// <summary> /// 每一个分支生成一个用例 /// </summary> /// <param name="process"></param> /// <returns></returns> public static List<FlowTestCase> GenerateTestCasesByProcess(Process process) { //找到开始流程 var startevent = process.Activities.FirstOrDefault(o => o.EventType == "StartEvent"); var lst = new List<FlowTestCase>(); var stack = new Stack<TempActivity>(); var root = new TempActivity(startevent, stack.ToList()); stack.Push(root); TempActivity current; while(stack.Count>0) { current = stack.Peek(); if (current.NextActivities.Count == 0) { //完成一条路径 FlowTestCase newcase = GetTestCase(stack.ToList(),process.Name, lst.Count); lst.Add(newcase); stack.Pop(); } else if (current.NextActivities.Count > current.IndexOfNextConnection) { var nextact = new TempActivity(current.NextActivities[current.IndexOfNextConnection], stack.ToList()); stack.Push(nextact); current.IndexOfNextConnection++; } else { //当前退栈 stack.Pop(); } } return lst; }
TempActivity的目的是在寻找过程中,记录当前节点中已经遍历的连接的位置,如果相关节点已经遍历完成,就出栈。为防止循环计算,本算法排除驳回类型的路径,也就是不考虑后续节点返回到之前路过的某一节点的情况。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 /// <summary> /// 20190417 用于计算路径得结构 /// </summary> public class TempActivity { public Activity MyActiviy { get; set; } public int IndexOfNextConnection { get; set; } /// <summary> /// 前面得活动,为了避免循环,需要去掉下级连接中返回得路径 /// </summary> private List<TempActivity> PrevActivities { get; set; } /// <summary> /// 下级节点,如果下级节点为0,说明形成最终路径 /// </summary> public List<Activity> NextActivities { get; set; } public TempActivity(Activity MyA,List<TempActivity> prevActivities) { MyActiviy = MyA; PrevActivities = prevActivities; NextActivities = new List<Activity>(); foreach(var conn in MyActiviy.OutConnections) { if (!prevActivities.Any(o => o.MyActiviy.Name == conn.TargetActivity.Name)) { NextActivities.Add(conn.TargetActivity); } } IndexOfNextConnection = 0; } }