Chapter 12. Testing a business process

A business process can be updated dynamically, which can cause errors, therefore testing a process business is also a part of the business process life cycle similar to any other development artifact.

The unit test for a business process ensures that the process behaves as expected in a specific use case. For example, you can test an output based on a particular input. To simplify unit testing, Red Hat Process Automation Manager includes the org.jbpm.test.JbpmJUnitBaseTestCase class.

The JbpmJUnitBaseTestCase performs as a base test case class, which is used for Red Hat Process Automation Manager related tests. The JbpmJUnitBaseTestCase provides the following usage areas:

  • JUnit life cycle methods

    Table 12.1. JUnit life cycle methods

    MethodDescription

    setUp

    This method is annotated as @Before. It configures a data source and EntityManagerFactory and deletes the session ID of a singleton.

    tearDown

    This method is annotated as @After. It removes history, closes EntityManagerFactory and a data source, and disposes RuntimeManager and RuntimeEngines.

  • Knowledge base and knowledge session management methods: To create a session, create RuntimeManager and RuntimeEngine. Use the following methods to create and dispose RuntimeManager:

    Table 12.2. RuntimeManager and RuntimeEngine management methods

    MethodDescription

    createRuntimeManager

    Creates RuntimeManager for a given set of assets and selected strategy.

    disposeRuntimeManager

    Disposes RuntimeManager that is active in the scope of the test.

    getRuntimeEngine

    Creates new RuntimeEngine for the given context.

  • Assertions: To test the state of assets, use the following methods:

    Table 12.3. RuntimeManager and RuntimeEngine Management Methods

    AssertionDescription

    assertProcessInstanceActive(long processInstanceId, KieSession ksession)

    Verifies whether a process instance with the given processInstanceId is active.

    assertProcessInstanceCompleted(long processInstanceId)

    Verifies whether a process instance with the given processInstanceId is completed. You can use this method if session persistence is enabled, otherwise use assertProcessInstanceNotActive(long processInstanceId, KieSession ksession).

    assertProcessInstanceAborted(long processInstanceId)

    Verifies whether a process instance with the given processInstanceId is aborted. You can use this method if session persistence is enabled, otherwise use assertProcessInstanceNotActive(long processInstanceId, KieSession ksession).

    assertNodeExists(ProcessInstance process, String…​ nodeNames)

    Verifies whether the specified process contains the given nodes.

    assertNodeActive(long processInstanceId, KieSession ksession, String…​ name)

    Verifies whether a process instance with the given processInstanceId contains at least one active node with the specified node names.

    assertNodeTriggered(long processInstanceId, String…​ nodeNames)

    Verifies whether a node instance is triggered for each given node during the execution of the specified process instance.

    assertProcessVarExists(ProcessInstance process, String…​ processVarNames)

    Verifies whether the given process contains the specified process variables.

    assertProcessNameEquals(ProcessInstance process, String name)

    Verifies whether the given name matches the specified process name.

    assertVersionEquals(ProcessInstance process, String version)

    Verifies whether the given process version matches the specified process version.

  • Helper methods: Use following methods to create a new RuntimeManager and RuntimeEngine for a given set of processes with or without using persistence. For more information about persistence, see Process engine in Red Hat Process Automation Manager.

    Table 12.4. RuntimeManager and RuntimeEngine Management Methods

    MethodDescription

    setupPoolingDataSource

    Configures a data source.

    getDs

    Returns the configured data source.

    getEmf

    Returns the configured EntityManagerFactory.

    getTestWorkItemHandler

    Returns a test work item handler that can be registered in addition to the default work item handler.

    clearHistory

    Clears the history log.

The following example contains a start event, a script task, and an end event. The example JUnit test creates a new session, starts the hello.bpmn process, and verifies whether the process instance is completed and the StartProcess, Hello, and EndProcess nodes are executed.

Figure 12.1. Example JUnit Test of hello.bpmn Process

Example JUnit Test Process
public class ProcessPersistenceTest extends JbpmJUnitBaseTestCase {

    public ProcessPersistenceTest() {
        super(true, true);
    }

    @Test
    public void testProcess() {

        createRuntimeManager("hello.bpmn");

        RuntimeEngine runtimeEngine = getRuntimeEngine();

        KieSession ksession = runtimeEngine.getKieSession();

        ProcessInstance processInstance = ksession.startProcess("com.sample.bpmn.hello");

        assertProcessInstanceNotActive(processInstance.getId(), ksession);

        assertNodeTriggered(processInstance.getId(), "StartProcess", "Hello", "EndProcess");
    }
}

JbpmJUnitBaseTestCase supports all predefined RuntimeManager strategies as part of the unit testing. Therefore, it is enough to specify the strategy that is used when you create a RuntimeManager as part of a single test. The following example shows the use of the PerProcessInstance strategy in a task service to manage user tasks:

public class ProcessHumanTaskTest extends JbpmJUnitBaseTestCase {

    private static final Logger logger = LoggerFactory.getLogger(ProcessHumanTaskTest.class);

    public ProcessHumanTaskTest() {
        super(true, false);
    }

    @Test
    public void testProcessProcessInstanceStrategy() {
        RuntimeManager manager = createRuntimeManager(Strategy.PROCESS_INSTANCE, "manager", "humantask.bpmn");
        RuntimeEngine runtimeEngine = getRuntimeEngine(ProcessInstanceIdContext.get());
        KieSession ksession = runtimeEngine.getKieSession();
        TaskService taskService = runtimeEngine.getTaskService();

        int ksessionID = ksession.getId();
        ProcessInstance processInstance = ksession.startProcess("com.sample.bpmn.hello");

        assertProcessInstanceActive(processInstance.getId(), ksession);
        assertNodeTriggered(processInstance.getId(), "Start", "Task 1");

        manager.disposeRuntimeEngine(runtimeEngine);
        runtimeEngine = getRuntimeEngine(ProcessInstanceIdContext.get(processInstance.getId()));

        ksession = runtimeEngine.getKieSession();
        taskService = runtimeEngine.getTaskService();

        assertEquals(ksessionID, ksession.getId());

        // let John execute Task 1
        List<TaskSummary> list = taskService.getTasksAssignedAsPotentialOwner("john", "en-UK");
        TaskSummary task = list.get(0);
        logger.info("John is executing task {}", task.getName());
        taskService.start(task.getId(), "john");
        taskService.complete(task.getId(), "john", null);

        assertNodeTriggered(processInstance.getId(), "Task 2");

        // let Mary execute Task 2
        list = taskService.getTasksAssignedAsPotentialOwner("mary", "en-UK");
        task = list.get(0);
        logger.info("Mary is executing task {}", task.getName());
        taskService.start(task.getId(), "mary");
        taskService.complete(task.getId(), "mary", null);

        assertNodeTriggered(processInstance.getId(), "End");
        assertProcessInstanceNotActive(processInstance.getId(), ksession);
    }
}

12.1. Testing integration with external services

Business processes often include the invocation of external services. Unit testing of a business process enables you to register test handlers that verify whether the specific services are requested correctly, and also provide test responses for the requested services.

To test the interaction with external services, use the default TestWorkItemHandler handler. You can register the TestWorkItemHandler to collect all the work items of a particular type. Also, TestWorkItemHandler contains data related to a task. A work item represents one unit of work, such as sending a specific email or invoking a specific service. The TestWorkItemHandler verifies whether a specific work item is requested during an execution of a process, and the associated data is correct.

The following example shows how to verify an email task and whether an exception is raised if the email is not sent. The unit test uses a test handler that is executed when an email is requested and enables you to test the data related to the email, such as the sender and recipient. Once the abortWorkItem() method notifies the engine about the email delivery failure, the unit test verifies that the process handles such case by generating an error and logging the action. In this case, the process instance is eventually aborted.

Figure 12.2. Example email process

Example email process for testing
public void testProcess2() {

    createRuntimeManager("sample-process.bpmn");

    RuntimeEngine runtimeEngine = getRuntimeEngine();

    KieSession ksession = runtimeEngine.getKieSession();

    TestWorkItemHandler testHandler = getTestWorkItemHandler();

    ksession.getWorkItemManager().registerWorkItemHandler("Email", testHandler);

    ProcessInstance processInstance = ksession.startProcess("com.sample.bpmn.hello2");

    assertProcessInstanceActive(processInstance.getId(), ksession);
    assertNodeTriggered(processInstance.getId(), "StartProcess", "Email");

    WorkItem workItem = testHandler.getWorkItem();
    assertNotNull(workItem);
    assertEquals("Email", workItem.getName());
    assertEquals("me@mail.com", workItem.getParameter("From"));
    assertEquals("you@mail.com", workItem.getParameter("To"));

    ksession.getWorkItemManager().abortWorkItem(workItem.getId());
    assertProcessInstanceNotActive(processInstance.getId(), ksession);
    assertNodeTriggered(processInstance.getId(), "Gateway", "Failed", "Error");

}