本章节,我们将给大家剖析两个流程测试类。一个是简单的基于内存模型的流程测试FirstFlowProcessTest;一个是更贴近实用的,基于MySQL数据库操作的标准测试案例。通过对这两个测试例程的分析,来直观的学习如何通过Java API操作jPDL。
简单流程测试案例
测试案例类:FirstFlowProcessTest.java
public class FirstFlowProcessTest extends TestCase {
ProcessDefinition pdf ;
ProcessInstance pi;
public void test4000YuanApplication() throws Exception {
deployProcessDefinition();
createProcessInstance("linly");
submitApplication(4000);
approveByManager(true);
checkTasks();
}
public void test6000YuanApplication() throws Exception {
deployProcessDefinition();
createProcessInstance("linly");
submitApplication(6000);
approveByManager(true);
approveByPresident(true);
checkTasks();
}
public void test7000YuanApplication() throws Exception {
deployProcessDefinition();
createProcessInstance("linly");
submitApplication(7000);
approveByManager(true);
approveByPresident(false);
checkTasks();
}
/**
* 部署流程定义
* @throws Exception
*/
protected void deployProcessDefinition() throws Exception{
System.out.println("==FirstFlowProcessTest.deployProcessDefinition()==");
pdf = ProcessDefinition.parseXmlResource("firstflow/processdefinition.xml");
assertNotNull("Definition should not be null", pdf);
}
/**
* 生成流程实例
*/
protected void createProcessInstance(String user){
System.out.println("==FirstFlowProcessTest.createProcessInstance()==");
assertNotNull("Definition should not be null", pdf);
//生成实例
pi = pdf.createProcessInstance();
assertNotNull("processInstance should not be null", pi);
//设置流程发起人
pi.getContextInstance().createVariable("initiator", user);
//触发流程转向
pi.signal();
}
/**
* 填写提交申请单
* @param money
*/
protected void submitApplication(int money){
System.out.println("==FirstFlowProcessTest.submitApplication()==");
TaskInstance ti = (TaskInstance)pi.getTaskMgmtInstance().getTaskInstances().iterator() .next() ;
System.out.println("ti.actor = " + ti.getActorId());
ContextInstance ci = ti.getContextInstance();
ci.setVariable("money",new Integer(money));
ti.end();
}
/**
* 部门经理审批
* @param pass
*/
@SuppressWarnings("unchecked")
protected void approveByManager(boolean pass){
System.out.println("==FirstFlowProcessTest.approveByManager()==");
Iterator<TaskInstance> it = pi.getTaskMgmtInstance().getTaskInstances().iterator();
for( ;it.hasNext(); ){
TaskInstance ti = it.next();
if(ti.getActorId().equals("DepartmentManager")){
List<Transition> transitions = ti.getToken().getNode().getLeavingTransitions();
for(Transition t : transitions){
System.out.println("----Transition" + t.getName());
}
assertEquals("DepartmentManager",ti.getActorId());
if(pass){
ti.end("部门经理审批通过");
}else{
ti.end("部门经理驳回");
}
return;
}
}
}
/**
* 总经理审批
* @param pass
*/
@SuppressWarnings("unchecked")
protected void approveByPresident(boolean pass){
System.out.println("==FirstFlowProcessTest.approveByPresident()==");
Iterator<TaskInstance> it = pi.getTaskMgmtInstance().getTaskInstances().iterator();
for( ;it.hasNext(); ){
TaskInstance ti = it.next();
if(ti.getActorId().equals("President")){
List<Transition> transitions = ti.getToken().getNode().getLeavingTransitions();
for(Transition t : transitions){
System.out.println("----Transition" + t.getName());
}
assertEquals("President",ti.getActorId());
if(pass){
ti.end("总经理审批通过");
}else{
ti.end("总经理驳回");
}
return;
}
}
}
/**
* 打印任务记录
*/
@SuppressWarnings("unchecked")
protected void checkTasks(){
System.out.println("==FirstFlowProcessTest.checkTasks()==");
Collection<TaskInstance> coll = pi.getTaskMgmtInstance().getTaskInstances();
System.out.println("====Process has task:====");
for(TaskInstance ti : coll){
System.out.println("=="+ti.getName()+"==");
System.out.println("=="+ti.getActorId()+"==");
System.out.println("=="+ti.getVariables().toString() +"==");
}
System.out.println("====end====");
}
该案例是在没有数据库支持的情况下,对报销流程进行运行测试,测试逻辑如下:
1. 加载流程定义
ProcessDefinition.parseXmlResource("firstflow/processdefinition.xml")
2. 实例化流程对象
//生成实例
pi = pdf.createProcessInstance();
assertNotNull("processInstance should not be null", pi);
//设置流程发起人
pi.getContextInstance().createVariable("initiator", user);
//触发流程转向
pi.signal();
代码说明:
在获得流程定义的实例后,可以用它生成流程实例,使用如下的语句:
pi = pdf.createProcessInstance();
流程实例拥有自己的ContextInstance环境变量对象。它实际上是一个HashMap,以key-value方式记录了流程的上下文变量值,代码中的
pi.getContextInstance().createVariable("initiator", user);就是向环境变量中添加一个key为initiator的对象。
每个流程实例都拥有自己Token令牌对象,主流程有自己的RootToken,子流程也拥有自己的子Token。父流程的Token和子流程的Token相互关联,形成Token树。
Token对象表示流程运行的当前位置(运行到哪个节点了)。通过对Token对象的signal()方法调用,可以使流程向下运行。代码中的pi.signal();实际上是间接调用了pi.getRootToken().signal();它使得新建的流程继续向下个节点(即借款申请单填写)进发。
3. 员工发起借款申请
/**
* 填写提交申请单
* @param money
*/
protected void submitApplication(int money){
System.out.println("==FirstFlowProcessTest.submitApplication()==");
TaskInstance ti = (TaskInstance)pi.getTaskMgmtInstance().getTaskInstances().iterator() .next()
System.out.println("ti.actor = " + ti.getActorId());
ContextInstance ci = ti.getContextInstance();
ci.setVariable("money",new Integer(money));
ti.end();
}
代码说明:
在借款流程发起后,流程进入了申请单填写阶段。这个阶段是个人工的任务,需要用户的介入。因此,对于要借款的用户而言,首先是获取填写申请单的任务实例:
TaskInstance ti = (TaskInstance)pi.getTaskMgmtInstance().getTaskInstances().iterator() .next()
在这个测试类中,由于没有数据库。对流程实例的引用是依靠了类的全局标量pi。这里通过pi获取全部的任务列表。实际上有且仅有一个任务,就是我们刚刚发起的申请单填写任务。
接下来,我们获取流程的上下文变量,将申请借款的数额记录在上下文变量中ContextInstance ci = ti.getContextInstance();
ci.setVariable("money",new Integer(money));
最后,我们要结束当前任务,告诉流程继续下行,调用ti.end(); 这个方法的本质依然是调用了token.signal(),它选择了一个默认的transition进行转向。这里要说明的是signal方法有多态的实现signal(Transition transition),是可以指定具体转向参数的。
4. 部门领导审批申请
/**
* 部门经理审批
* @param pass
*/
@SuppressWarnings("unchecked")
protected void approveByManager(boolean pass){
System.out.println("==FirstFlowProcessTest.approveByManager()==");
Iterator<TaskInstance> it = pi.getTaskMgmtInstance().getTaskInstances().iterator();
for( ;it.hasNext(); ){
TaskInstance ti = it.next();
if(ti.getActorId().equals("DepartmentManager")){
List<Transition> transitions = ti.getToken().getNode().getLeavingTransitions();
for(Transition t : transitions){
System.out.println("----Transition" + t.getName());
}
assertEquals("DepartmentManager",ti.getActorId());
if(pass){
ti.end("部门经理审批通过");
}else{
ti.end("部门经理驳回");
}
return;
}
}
}
代码说明:
这里,流程进入了部门经理审批阶段。由于没有数据库支持,我们只能采取遍历任务列表,并比对委派者ID的方式来确定委派给部门经理的任务实例。(在后面的基于数据库的标准案例中,我们会看到如果根据用户的ID来获取分配给指定用户的任务)
ti.getActorId().equals("DepartmentManager") // 比对任务的委派人。
ti.getToken().getNode().getLeavingTransitions();//获取任务在当前节点上的所有转向。
这里我们要特别指出的是ti.end("部门经理审批通过")和ti.end("部门经理驳回")这实际上调用token.signal(transition);来完成任务的转向,从而使流程继续。
5. 总经理审批申请
/**
* 总经理审批
* @param pass
*/
@SuppressWarnings("unchecked")
protected void approveByPresident(boolean pass){
System.out.println("==FirstFlowProcessTest.approveByPresident()==");
Iterator<TaskInstance> it = pi.getTaskMgmtInstance().getTaskInstances().iterator();
for( ;it.hasNext(); ){
TaskInstance ti = it.next();
if(ti.getActorId().equals("President")){
List<Transition> transitions = ti.getToken().getNode().getLeavingTransitions();
for(Transition t : transitions){
System.out.println("----Transition" + t.getName());
}
assertEquals("President",ti.getActorId());
if(pass){
ti.end("总经理审批通过");
}else{
ti.end("总经理驳回");
}
return;
}
}
}
代码说明:
此步代码同“部门经理审批”代码相似,不作更多说明。