跳到导航
dev2dev.bea.com.cn
首页 资源中心 dev2dev学堂 在线技术论坛 专家Blog User Group CodeShare
dev2dev 首页 > 资源中心 > 技术文章
使用JProcessUnit测试业务流程简介

时间:2006-08-07
作者:Reza Shafii
浏览次数:
本文关键字:jprocessunittestjunitweblogic integrationjpdWebLogic IntegrationWebLogic WorkshopReza Shafii测试业务流程
文章工具
推荐给朋友 推荐给朋友
打印文章 打印文章

  在软件开发领域中,对软件组件进行自动化测试已经被公认为是一项最佳实践。可以使用BEA WebLogic Workshop 8.1测试浏览器对BEA WebLogic Integration 8.1 Java流程定义(Java Process Definition,JPD)进行手动测试。然而,该解决方案没有为开发人员提供编写基于JUnit的自动化测试用例。本文将介绍JProcessUnit API CodeShare项目,并演示如何使用它来为WebLogic Integration JPD创建定义良好的、基于JUnit的自动化测试用例。

测试Java流程定义的原因

  自动化测试有助于开发出高质量的软件应用程序。这类测试允许重复执行和验证应用程序的各个元素和方面,这种方法无疑可以降低出现未检测到的软件错误的可能性。因此,为了提高软件质量,应该为尽可能多的软件组件编写自动化测试。此外,涉及到的软件组件越关键,编写自动化测试验证其功能就显得越发重要。

  Java流程定义抽象了重要的业务集成元素,比如数据转换、贸易伙伴集成、连通性和消息代理。此外,通过编排内部应用程序组件、最终用户和外部系统或服务之间的交互,JPD建模并自动化了企业业务流程。这种能力使得JPD在复合应用程序的开发过程中发挥了关键作用,BEA SOA域模型中对于这一点进行了详细说明。由于提供了这些关键功能,JPD的自动化测试对于提高WebLogic Integration应用程序的质量显得颇为重要。

  在了解了JPD自动化测试的优点之后,我将进一步详细说明如何使用JProcessUnit API来实现它。请注意,本文假定读者对JUnit框架有一定的了解。

如何使用JProcessUnit测试业务流程

  如图1中的类图所示,JProcessUnit 测试用例是基于JUnit 框架的。一个JProcessUnit ProcessTestCase 类扩展了JUnit TestCase 类。通过使用ActionEvent 和 WaitEvent子类(它们其实是ProcessEvent类的子类)的实例, ProcessTestCase 的测试方法可以启动WebLogic Integration JPD并与其交互。

图 1
图 1. JProcessUnit类图

  ActionEvent 子类用于启动一个JPD,并执行JPD实例将对其做出响应的任务。这类任务包括创建一个数据库项,发布一条消息到消息通道,或者修改某个特定的工作列表任务状态。

  WaitEvent子类用于等待来自某个JPD实例的特定结果(或检验它的出现)。这类事件的例子有:JPD创建数据库项、消息通道中消息的到达或创建特定的工作列表任务。

  图 2 说明了在JProcessUnit测试方法中,如何使用一组ActionEvent 和 WaitEvent 子类实例来创建特定的测试用例。

图 2
图 2. JProcessUnit测试用例交互

  借助于JProcessUnit测试范型,ProcessEvent类用于模拟JPD和外部系统之间的消息交换(在图2中用①标记的区域)。然而,如果JPD和外部系统之间的集成是通过松散耦合的方法(比如消息收发或文件交换)来实现的,这种做法可以达到最佳效果。我们这样说是基于以下的事实:这类集成通常允许我们编写ProcessEvent子类,该子类就像是JDP要与之轻松交互的目标系统。这是通过使用WaitEvent检测JPD生成的消息(或文件)以及使用ActionEvent创建正确的响应消息来实现的。

  如果JPD与外部系统之间的集成是通过耦合更加紧密的、基于RPC的协议来实现的,那么创建模拟系统的ProcessEvent子类就会变得更加复杂。这是因为以下这个事实:我们不再能够轻松检测到通往目标系统的出站JPD调用。在这种情况下,一种替代方案是使用JPD可以与之交互的测试系统(见图2中用②标记的区域)。

  还要注意,在构造JProcessUnit测试用例的过程中,不必担心所涉及的JPD的数量。相反,我们可以测试通过启动特定JPD而启动的业务流程,无论这个JPD是否涉及到子JPD。这种思路在图2中已经说明,请参见其中的一个针对涉及到JPD 1和2的业务流程的测试用例。

  对于这个范型,分析人员应该注意到一个重要的问题:在执行测试之后,如何确保正确清除服务器工件?例如,如果在流程创建一个任务之后,测试用例失败,我们不会希望使该任务保持挂起状态,而是希望删除它。为解决这个问题,可以选择使用JProcessUnit的撤销功能。这项功能允许把一系列的JProcessUnit事件构建为撤销事件。然后,通过在测试用例的tearDown()方法中调用ProcessTestCase的runUndoEvent()方法,可以以添加这些撤销事件的顺序来执行它们。撤销功能允许我们创建在执行之后可以清除的测试用例。

  现在,对于如何使用JProcessUnit事件测试WebLogic Integration业务流程,您想必已经有了更好的了解,接下来,我将简要探讨这些事件的内部工作机制。注意,本文中没有详细说明JProcessUnit事件,想要了解详细信息,可以参考项目的API 文档

内部工作机制

  JProcessUnit API中的大多数现有JPD等待和动作事件都使用ProcessRuntimeMBeanRemoteWorklistManager JMX MBean来实现其大部分(但是并非全部)功能。事实上,除了ReturnProcessWaitEvent等待事件之外,其他所有的特定于JPD的事件都可以视为提供了一个结构化的外观来实现上述MBean。

  首先来看一看使用ProcessRuntimeMBean MBean的事件,它们是StartProcessEventTerminateProcessEventProcessStateWaitEvent。在这3个事件中,StartProcessEvent是最基本的,因为它允许启动新的JPD实例,而且使用其他两个事件时也需要用到它。

  用于启动JPD消息(其中包含了要传递给JPD的特定客户端响应节点的数据)的值是初始化StartProcessEvent事件所需的重要信息。StartProcessEvent需要这个值作为JPD的Web服务接口的SOAP主体的内容。幸运的是,有一种简单的方法可以捕捉所需的结构。只需运行JPD的测试浏览器(通过单击WebLogic Workshop中的start按钮),然后导航到JPD的Test SOAP选项卡。该页面上的文本框包含了所需SOAP主体内容的一个预填充的XML例子。StartProcessEvent使用这些信息来构建正确的SOAP消息,并调用目标JPD的预定Web服务接口。

  启动JPD之后, StartProcessEvent 事件可以用于包含它的有价值信息和任意子JPD。这些信息包括:

  • 内部变量的值。
  • 任意JPD的状态。
  • 任意JPD的标签。
  • 任意JPD的流程实例ID。

  流程实例ID尤其有用,因为TerminateProcessEvent和ProcessStateWaitEvent需要使用它来惟一地标识目标JPD实例。

  在当前的API中,基于RemoteWorklistManager MBean的事件有CompleteTaskEventDeleteTaskEventTaskWaitEvent。所有这些事件都使用了WebLogic Integration API中的TaskSelector类来定义它们的目标任务集。

  这里面没有包括ReturnProcessWaitEvent等待事件,该事件用于通过一个客户端响应节点来检查JPD的响应。该事件的独特之处在于,它使用嵌入式Web Server(使用Jetty实现),通过一个来自(由StartProcessEvent事件启动的)JPD的Web服务回调来接收和跟踪任意返回消息。

  最后,有必要更深入地分析WaitEvent类。该类间歇地调用其waitConditionSatisfied()方法。WaitEvent的子类只需重载这个方法,并对相应的资源执行检查(例如,是否创建了数据库项,或者特定的JPD是否进入了某种特定状态)即可。WaitEvent调用该方法的频率和次数是可配置的。

  了解了当前JProcessUnit事件的底层实现之后,现在我将给出一个应用它们的例子。

例子

  用于创建一组JProcessUnit测试用例的示例WebLogic Integration应用程序涉及到一个员工请假的业务流程,该业务流程由2个JPD组成。图3说明了这个业务流程的顶级JPD。

图 3
图 3.示例请假JPD

  显然,这个流程是虚构的,而且有意构造得很简单。然而,它应该足以演示创建JProcessUnit测试用例时涉及到的许多关键理念。现在,我将针对这个流程的两条执行路径来介绍如何创建测试用例。一条路径是验证主管批准了请假要求,而另一条则是没有批准。这两条执行路径都涉及到HR系统认为请求可行的用例。

创建测试方法

  一开始,我创建了一些JProcessUnit测试方法。其中每个方法都测试了一条流程执行路径:

/**
 * Tests the supervisor approval scenario for the time off request process
 * (ProcessTimeOffRequest.jpd and IsTimeOffFeasible.jpd).
 */
public void testApprovedExpense() {

    startTimeOffProcess(createTestTimeOffRequestXML().toString());

    assertFeasibility();

    completeTimeOffApprovalTask(true);

    verifyProcessReturn(true);
}

/**
 * Tests the supervisor denial scenario for the time off request process
 * (ProcessTimeOffRequest.jpd and IsTimeOffFeasible.jpd).
 */
public void testDeniedExpense() {

    startTimeOffProcess(createTestTimeOffRequestXML().toString());

    assertFeasibility();

    completeTimeOffApprovalTask(false);

    verifyProcessReturn(false);
}

  正如您看到的那样,每个测试方法都只调用了一组实用工具方法,其顺序完全一致,但使用不同的参数值。事实上,这些实用工具方法执行了更为复杂的工作,即执行与JPD进行交互并验证其功能的正确ActionEvent和WaitEvent子类。一种创建流程测试用例的好方法是针对每个节点(或一组相关节点)的测试实现一个实用工具方法,而我也正是这样做的。然后,不同的测试用例可以使用这些方法测试流程的不同执行路径。在下一部分内容中,我将介绍如何实现这些实用工具方法。

创建实用工具方法

  一开始,我创建了用于启动流程实例的方法:

private void startTimeOffProcess(String timeOffRequestXML) {

    // Create the start event
    startProcessTimeOffRequest = new StartProcessEvent(SERVICE_URI_PREFIX,
            IS_PROCESSTIMEOFFREQUEST_JPD);
    startProcessTimeOffRequest.setSoapMessage(timeOffRequestXML);

    // Add an undo event to terminate the process
    TerminateProcessEvent terminateProcess = new TerminateProcessEvent(
            startProcessTimeOffRequest);
    this.addUndoEvent(terminateProcess);

    // Assert event and give the process time to get started and running
    startProcessTimeOffRequest.assertEvent();
    waitForProcess(1000);
}

  正如您看到的那样,一开始,我使用ProcessTimeOffRequest.jpd流程的父JPD的相关信息创建了一个StartProcessEvent实例。接着,我为同一个JPD创建了一个TerminateProcessEvent,然后把它设置为一个撤销事件。这将确保无论测试成功还是失败,都将尝试终止由StartProcessEvent创建的JPD实例。

  借助于创建的这个实用工具方法,可以继续为流程的下一部分创建一个实用工具方法,执行调用到一个虚构HR系统的Web服务(在这个例子中,就是始终返回true的Web服务)的IsTimeOffFeasible.jpd,以检查请假请求的可行性。

private void assertFeasibility() {

    // Create a wait even for the IsTimeOffFeasible child JPD.
    String isFeasibleId = startProcessTimeOffRequest
            .getInstanceID(SERVICE_URI_PREFIX + "/" + IS_TIMEOFFFEASIBLE_JPD);
    ProcessStateWaitEvent completeWaitEvent = new ProcessStateWaitEvent(
                isFeasibleId, ProcessStatus.COMPLETED);

    // Create a terminate undo event for this JPD
    TerminateProcessEvent terminateProcess = new TerminateProcessEvent(
        SERVICE_URI_PREFIX + "/" + IS_TIMEOFFFEASIBLE_JPD, isFeasibleId);
    this.addUndoEvent(terminateProcess);

    // Wait until the child JPD has completed execution
    completeWaitEvent.assertEvent();

    // Check that the request was feasible through an internal variable.
    String isFeasible = startProcessTimeOffRequest.getStringVariableValue(
                SERVICE_URI_PREFIX + "/" + IS_TIMEOFFFEASIBLE_JPD,
                FEASIBLE_VAR_NAME);
    assertEquals(isFeasible, "true");
}

  该方法首先创建了一个ProcessStateWaitEvent实例,以检查(子)IsTimeOffFeasible.jpd实例的完成情况。在断言这个事件之前,我创建了一个用于终止子实例JPD的撤销事件。在成功断言IsTimeOffFeasible.jpd已经完成之后,我可以获得一个内部变量的值,从而检查HR系统返回的结果,并确保它被设置为true。

  现在,我已经准备好为测试主管对请假请求的批准情况创建实用工具方法了。

private void completeTimeOffApprovalTask(boolean approved) {

    // The process creates a task for the supervisor with the employee ID
    // field. We create a selector for this task to use for all task events.
    TaskSelector selector = new TaskSelector();
    selector.setTaskName(SUPERVISOR_TASK_NAME, false);

    // Wait until the supervisor task is created
    TaskWaitEvent taskCheck = new TaskWaitEvent(selector);
    taskCheck.assertEvent();

    // Create an undo task to make sure that the task is deleted
    DeleteTaskEvent deleteTask = new DeleteTaskEvent(selector);
    this.addUndoEvent(deleteTask);

    // Complete task with an approval response.
    CompleteTaskEvent approveRequest = new CompleteTaskEvent(selector);
    approveRequest.setTaskResponse(createApprovalResponseXML(approved).toString());
    approveRequest.assertEvent();
}

  该方法验证了与流程相关的WebLogic Integration工作列表任务的创建、完成和结果。该任务是由用于员工主管的流程创建的,它包含对于请假请求的批准(或不批准)。在这个方法的开头部分,我实例化了一个TaskWaitEvent类,以检查主管任务的创建情况。当我成功验证这个任务的创建情况之后,我创建了一个DeleteTaskEvent事件实例,并把它设置为一个撤销事件。我通过实例化一个CompleteTaskEvent对象来终止该方法。这个动作事件允许我通过一个工作列表用户界面有效地模拟某个人(在这个例子中就是主管)的任务完成情况。

  我要创建的最后一个实用工具方法是用于检查请假流程的成功响应的:

private void verifyProcessReturn(boolean approval) {

    // Wait until the process returns with a response
    ReturnProcessWaitEvent timeOffResponse = new ReturnProcessWaitEvent(
                RESPONSE_METHOD_NAME, startProcessTimeOffRequest);
    timeOffResponse.assertEvent();

    // Compare returned response to the expected response
    XmlObject actualResponse = timeOffResponse.getResponseContent();
    XmlObject expectedResponse = createApprovalResponseXML(approval);

    assertEquals(expectedResponse.toString(), actualResponse.toString());
}

  我可以通过使用我所关心的响应节点的名称(即用于JPD的Client Response节点方法的名称)和用于启动流程的StartProcessEvent对象来实例化一个ReturnProcessWaitEvent对象。一旦我知道了流程成功做出响应,我就可以通过getResponseContent()方法来获得其响应消息,从而检查其内容。在这个例子中,我是通过基于实用工具方法的参数构建一个预期响应(批准或不批准),并将其与来自流程的实际响应消息进行比较来实现这一点的。

  现在,我已经为验证(希望为其创建测试用例的)两条执行路径中包含的节点创建了实用工具方法。

配置测试环境

  在运行任何测试用例之前,必须对JProcessUnit环境进行配置,以便让它可以与驻留目标JPD的正确WebLogic Integration服务器实例进行交互。这是通过修改JProcessUnitConfig.xml文件并确保它位于JUnit项目的类路径中来实现的。有关这个文件和测试环境所需的其他JAR文件配置的详细指南,可以在JProcessUnit下载包文档中找到,因此这里不再赘述。完成配置之后,就可以在JUnit执行环境中运行测试了。

测试注意事项

  至此,我对JProcessUnit框架的介绍就结束了,您可以开始编写一些自己的测试用例,同时我希望您可以记住以下的事项。

  首先,有一个事实相当重要,即JProcessUnit提供的是一种黑盒测试方法。JProcessUnit测试从流程面向外部的接口测试流程的功能。这类似于HttpUnit之类的测试框架,在这些框架中,用户界面是从用户而不是业务流程的角度进行测试的。

  系统外部接口的黑盒测试颇为重要(关于其原因的详细分析,请参见这篇有趣的文章)。然而,这类测试的创建和维护可能会更加棘手,JProcessUnit也不例外。因此,为每个流程的每条执行路径创建测试可能不是一种合算的方法。因此,在大多数情况下,为每个流程编写少量的测试用例来测试主要路径的执行就显得更为合理。然后,这些测试可以用于简单回归,以确保在出现变化时系统依然保持稳定。

  其次,还应注意,JProcessUnit框架还绝称不上完善。无疑,借助于其新功能和必需的设计更改,这个框架应该能够随需求的变化而改进。更重要的是,尽管当前的事件集合对于执行与JPD的交互已经足够,但是一些关键元素(比如用于测试动态消息代理订阅的事件)仍然缺失,需要加以实现。当前API所包含的流程测试事件集很可能会随时间而增长,因为该框架的用户开始编写更好地适应其即时需求的测试事件。

  最后,您可能已经注意到了,迄今为止所讨论的测试技术应该适用于测试运行在任何业务流程引擎(Business Process Engine,BPE)中的任何类型的自动化业务流程,而不仅仅是WebLogic Integration JPD。这是因为,JProcessUnit API的核心设计没有就所使用的底层业务流程引擎技术做任何假设。可以使用特定于某种技术的ActionEvent和WaitEvent子类来扩展这些API。JProcessUnit的当前版本包含两种不同类型的扩展:一组BPE中立的事件,用于简单的、一般性的File和Database流程交互,一组特定于WebLogic Integration的事件,用于与JPD进行交互,后者我在本文中已经提及。

下载

  您可以从CodeShare 项目下载JProcessUnit的完整源代码。

  还可以下载JProcessUnitExample.zip文件,该文件包含了本文中使用的完整示例应用程序。

结束语

  在本文中,我介绍了JProcessUnit CodeShare项目,该项目允许为业务流程(具体地说,是WebLogic Integration Java流程定义)创建基于JUnit的自动化测试。本文说明了为JPD创建自动化测试的优点,并介绍了使用JProcessUnit创建这类测试的整个过程。

参考资料

原文出处:http://dev2dev.bea.com/pub/a/2006/04/process-testing.html

 作者简介
Reza Shafii是BEA 公司的首席顾问。他的主要兴趣在于企业应用集成 (EAI) 领域,特别是 Web services 技术的实践和高效使用。 Reza 拥有 渥太华 大学的工商管理硕士 (MBA) 学位和 英属哥伦比亚大学 的计算机科学学士学位。
dot dot dot

dot
  作者其它文章
您对本文的评价
您对这篇文章的看法如何?
太棒了!5分 不错啊 4分 一般般 3分 有待提高 2分 不好 1分