Chapter 11. Testing and Debugging

11.1. Unit Testing

11.1.1. Unit Testing

Business Processes should be designed at a high level with no implementation details; however, just like other development artifacts, they still have a lifecycle; since business processes can be updated dynamically, it is important that they are tested.
Unit tests are conducted to ensure processes behave as expected in specific use cases, for example, to test the output based on the specific input. The helper class JbpmJUnitTestCase (in the jbpm-test module) has been included to simplify unit testing. JbpmJUnitTestCase provides the following:
  • Helper methods to create a new knowledge base and session for a given set of processes.
  • Assert statements to check:
    • The state of a process instance (active, completed, aborted).
    • Which node instances are currently active.
    • Which nodes have been triggered (to check the path that has been followed).
    • The value of variables.
The image below contains a start event, a script task, and an end event. Within the example junit Test, a new session is created, the process is started, and the process instance is verified based on successful completion. It also checks whether these three nodes have been executed.
An example hello world process.

Figure 11.1. Example Hello World Process

Example 11.1. example junit Test

public class MyProcessTest extends JbpmJUnitTestCase {

   public void testProcess() {
       // create your session and load the given process(es)
       StatefulKnowledgeSession ksession = createKnowledgeSession("sample.bpmn");
       // start the process
       ProcessInstance processInstance = ksession.startProcess("com.sample.bpmn.hello");
       // check whether the process instance has completed successfully
       assertProcessInstanceCompleted(processInstance.getId(), ksession);
       // check whether the given nodes were executed during the process execution
       assertNodeTriggered(processInstance.getId(), "StartProcess", "Hello", "EndProcess");
    }
}

11.1.2. Helper Methods to Create Sessions

Several methods are provided to simplify the creation of a knowledge base and a session to interact with the engine:
createKnowledgeBase(String... process):
Returns a new knowledge base containing all the processes in the given filenames (loaded from the classpath).
createKnowledgeBase(Map<String, ResourceType> resources):
Returns a new knowledge base containing all the resources from the given filenames (loaded from the classpath).
createKnowledgeBaseGuvnor(String... packages):
Returns a new knowledge base containing all the processes loaded from Guvnor (the process repository) from the given packages.
createKnowledgeSession(KnowledgeBase kbase):
Creates a new stateful knowledge session from the given knowledge base.
restoreSession(StatefulKnowledgeSession ksession, boolean noCache):
Completely restores this session from the database; it can be used to recreate a session to simulate a critical failure and test recovery. If noCache is true, the existing persistence cache will not be used to restore the data.

11.1.3. Assertions

The following assertions are provided to simplify testing the current state of a process instance:
assertProcessInstanceActive(long processInstanceId, StatefulKnowledgeSession ksession):
Checks whether the process instance with the given ID is still active.
assertProcessInstanceCompleted(long processInstanceId, StatefulKnowledgeSession ksession):
Checks whether the process instance with the given ID has completed successfully.
assertProcessInstanceAborted(long processInstanceId, StatefulKnowledgeSession ksession):
Checks whether the process instance with the given ID was aborted.
assertNodeActive(long processInstanceId, StatefulKnowledgeSession ksession, String... name):
Checks whether the process instance with the given ID contains at least one active node with the given node name (for each of the given names).
assertNodeTriggered(long processInstanceId, String... nodeNames):
Checks for each given node name whether a node instance was triggered (but not necessarily active anymore) during the execution of the process instance with the given node name (for each of the given names).
getVariableValue(String name, long processInstanceId, StatefulKnowledgeSession ksession):
Retrieves the value of the variable with the given name from the given process instance; it can then be used to check the value of process variables:

11.1.4. Testing Integration with External Services

Using domain-specific processes makes it possible to use testing handlers to verify whether or not specific services are requested correctly.
A TestWorkItemHandler is provided by default that can be registered to collect all work items (each work item represents one unit of work, for example, sending q specific email or invoking q specific service, and it contains all the data related to that task) for a given type. The test handler can be queried during unit testing to check whether specific work was actually requested during the execution of the process and that the data associated with the work was correct.
The following example describes how a process that sends an email could be tested. The test case tests whether an exception is raised when the email could not be sent (which is simulated by notifying the engine that sending the email could not be completed). The test case uses a test handler that simply registers when an email was requested and the data associated with the request. When the engine is notified the email could not be sent (using abortWorkItem(..)), the unit test verifies that the process handles this case successfully by logging this and generating an error, which aborts the process instance in this case.
An example image that illustrates how an email process could be tested.
public void testProcess2() {
    // create your session and load the given process(es)
    StatefulKnowledgeSession ksession = createKnowledgeSession("sample2.bpmn");
    // register a test handler for "Email"
    TestWorkItemHandler testHandler = new TestWorkItemHandler();
    ksession.getWorkItemManager().registerWorkItemHandler("Email", testHandler);
    // start the process
    ProcessInstance processInstance = ksession.startProcess("com.sample.bpmn.hello2");
    assertProcessInstanceActive(processInstance.getId(), ksession);
    assertNodeTriggered(processInstance.getId(), "StartProcess", "Email");
    // check whether the email has been requested
    WorkItem workItem = testHandler.getWorkItem();
    assertNotNull(workItem);
    assertEquals("Email", workItem.getName());
    assertEquals("me@mail.com", workItem.getParameter("From"));
    assertEquals("you@mail.com", workItem.getParameter("To"));
    // notify the engine the email has been sent
    ksession.getWorkItemManager().abortWorkItem(workItem.getId());
    assertProcessInstanceAborted(processInstance.getId(), ksession);
    assertNodeTriggered(processInstance.getId(), "Gateway", "Failed", "Error");
}

11.1.5. Configuring Persistence

By default, persistence is turned off and processes exist in memory.
To turn persistence on, pass a boolean to the super constructor when creating the test case and setPersistence to true as shown below:
public class MyProcessTest extends JbpmJUnitTestCase {

    public MyProcessTest() {
        // configure this test to use persistence
        super(true);
        setPersistence(true);
    }
    ...