JBoss Community

JUnit test support for jBPM5

modified by Maciej Swiderski in jBPM - View the full document

There are number of issues that people are facing while working with jBPM and to get help the most useful elements are:

  • process definition (BPMN2 file)
  • test case that illustrates mentioned issue

 

As process definition is concerned that is rather straight forward (since we already working with it when encountering the problem),

but with test case it can be bit more complex to give directly to others the driver we use for our case.

 

So, after struggling for some time with exactly the same issue, I decided to start with small but yet powerful support for test cases for jBPM.

There is already a test case class that we could extend and that already brings some useful elements but that is not all we need...

 

I tried to build up a small library that is dedicated to test cases based on JUnit and that aligns with latest releases of JUnit, meaning it is

based on annotations. So to move directly what I am referring to, lets take a look at very simple test case:

 

 

@RunWith(JbpmJUnitRunner.class)
@KnowledgeBase(source={"script.bpmn2"})
@KnowledgeSession()
public class JbpmJUnitTestCase {
 
    protected org.drools.KnowledgeBase knowledgeBase;
    protected StatefulKnowledgeSession session;
 
    @Test
    public void testScriptTask() {
        ProcessInstance instance = session.startProcess("ScriptTask");
        assertProcessInstanceComplete(instance);
    }
}

 

What you can see here is the most basic test case that allows to execute and assert process instance completion. I think the code is self explanatory so

I jump directly to the annotations that were introduced with this library:

@RunWith - this one is standard JUnit annotation that instructs what runner should be used to execute tests

@KnowledgeBase - annotation that defines what artifacts should be loaded into knowledge base

    Annotation attributes:

  •     source - list of resources to be loaded (currently only BPMN2 resources are supported)
  •     sharedKey - name of the key that will be used to cache knowledge based between test cases, so if you have set of resources that are used across various

                test cases and you want to avoid building knowledge base for each of the test cases sharedKey attribute should be set to load knowledge base only one time

 

@KnowledgeSession - annotation that defines how stateful sessions should be created and maintained

    Annotation attributes:

  •     disposePerTest - defines if session should be disposed after each test within test case, default set to true
  •     logger - defines what working memory logger should be configured for the session, available options NONE (default), CONSOLE, JPA
  •     handlers - set of work item handlers that will be registered for each session (WorkItemHandler annotation)
  •     persistence - defines persistence for the session (Persistence annotation)

 

        @Persistence annotation attributes:

  •     persistenceUnit - name of the persistence unit that should be used while configuring JPA entity manager factory
  •     reloadSession - defines if session should be reloaded (with the same id) between test invocation within test case, default false

 

        @WorkItemHandler annotation attributes:

  •      taskName - name of the task the handler is registered for
  •      handles - class of the handler

 

@HumanTaskSupport - annotation that instructs runner that test case requires Human Task interactions so it will initialize Task Server accessible via MINA transport

            it will be started on test case scope, meaning that all tests will have access to the same instance of the server within test case.

            Annotation attributes:

  •             users - list of user ids that should be added to the task server on startup
  •             groups - list of groups that should be added to the task server on startup
  •             persistanceUnit - name of the persistence unit that should be used while building entity manager factory
  •             host - interface on which task server should be started, defaults to localhost
  •             port - port on which task server should be started, defaults to 9123

 

 

In addition to annotation there are two property files required when configuring session persistence or human task server:

  • datasource.properties - defines parameters of connection pool
  • jndi.properties - defines JNDI provider for bitronix

 

NOTE: Connection pool will be created for entire test phase once datasource.properties file was discovered.

 

Obviously following files are required as well, since they are required by jbpm and task server artifacts:

  • META-INF/persistence.xml that defines both (jbpm and human task persistence units)
  • META-INF/JBPMorm.xml
  • META-INF/Taskorm.xml

 

Main concept is around injecting dependencies into your test case class. As dependencies I mean following items:

  • Knowledge base
  • Stateful session
  • Task client

 

Methods from JUnit life cycle are still available, such as these annotated with @Before, @After, @BeforeClass, @AfterClass. Instance methods (@Before @After) will

have access to session and knowledge base, for instance if some facts should be inserted before running the test.

 

There is one more item that is worth to mention, an assert class dedicated to jbpm - JbpmAssert, which defines (at the moment):

  • assertProcessInstanceComplete
  • assertProcessInstanceActive
  • assertProcessInstanceAborted
  • assertProcessInstancePending
  • assertProcessInstanceSuspended

 

and most likely more to come...

 

And now, its time to show some examples of it

 

Simple test case with session persistence:

 

@RunWith(JbpmJUnitRunner.class)
@KnowledgeBase(source={"script.bpmn2"})
@KnowledgeSession(persistence=@Persistence(persistenceUnit="org.jbpm.persistence.jpa"))
public class JbpmJUnitTestCase {
 
    protected org.drools.KnowledgeBase knowledgeBase;
    protected StatefulKnowledgeSession session;
 
    @Test
    public void testScriptTask() {
        ProcessInstance instance = session.startProcess("ScriptTask");
        assertProcessInstanceComplete(instance);
    }
}

 

If session reload is desired between tests test case should be defined as follows:

 

 

@RunWith(JbpmJUnitRunner.class)
@KnowledgeBase(source={"script.bpmn2"})
@KnowledgeSession(persistence=@Persistence(persistenceUnit="org.jbpm.persistence.jpa", reloadSession=true))
public class JbpmJUnitTestCase {
 
    protected org.drools.KnowledgeBase knowledgeBase;
    protected StatefulKnowledgeSession session;
 
    @Test
    public void testScriptTask() {
        ProcessInstance instance = session.startProcess("ScriptTask");
        assertProcessInstanceComplete(instance);
    }
    @Test
    public void testScriptTask2() {
        ProcessInstance instance = session.startProcess("ScriptTask");
        assertProcessInstanceComplete(instance);
    }
}

 

Both tests will use same session.

 

Last but not least, human interaction test case:

 

 

@RunWith(JbpmJUnitRunner.class)
@KnowledgeBase(source={"script.bpmn2","usertask.bpmn2"}, sharedKey="common")
@KnowledgeSession(handlers={@WorkItemHandler(taskName="Human Task", handler=WSHumanTaskHandler.class)}, logger=Logger.CONSOLE)
@HumanTaskSupport(persistenceUnit="org.jbpm.task", users={"john", "Administrator"})
public class JbpmJUnitRunnerTest {
 
    protected org.drools.KnowledgeBase knowledgeBase;
    protected StatefulKnowledgeSession session;
    protected TaskClient taskClient;
 
    @Test
    public void testHumanTask() throws Exception {
        ProcessInstance instance = session.startProcess("UserTask");
        Thread.sleep(1000);
        BlockingTaskSummaryResponseHandler taskSummaryHandler = new BlockingTaskSummaryResponseHandler();
        taskClient.getTasksAssignedAsPotentialOwner("john", "en-UK", taskSummaryHandler);
 
        assertEquals(1, taskSummaryHandler.getResults().size());
        TaskSummary task1 = taskSummaryHandler.getResults().get(0);
 
        BlockingTaskOperationResponseHandler taskOperationHandler = new BlockingTaskOperationResponseHandler();
        taskClient.claim(task1.getId(), "john", taskOperationHandler);
 
        taskOperationHandler = new BlockingTaskOperationResponseHandler();
        taskClient.start(task1.getId(), "john", taskOperationHandler);
        taskOperationHandler.waitTillDone(1000);
 
        taskOperationHandler = new BlockingTaskOperationResponseHandler();
        taskClient.complete(task1.getId(), "john", null, taskOperationHandler);
        taskOperationHandler.waitTillDone(1000);
 
        Thread.sleep(1000);
 
        assertProcessInstanceComplete(instance);
    }
}

 

In the example we can notice several configurations elements:

  • knowledge base is configured to be shared between test cases under 'common' shared key
  • knowledge session will have registered handler for 'Human Task' work items
  • knowledge session will have a console logger configured
  • human task server will be configured with 'org.jbpm.task' persistence unit and will add two users once it's started (john, Administrator)

 

Ok, so that is it! I hope you can provide some feedback about it and hopefully we can get a valuable discussion around it and what's even more important

maybe we start using it on daily basis so everybody could benefit from it.

Attached draft version of the library.

 

Looking forward to your comments

Comment by going to Community

Create a new document in jBPM at Community