Running Camunda BPM Process Tests with Container Managed CDI Beans
BPM projects often start with a simple process model. But from experience the process model complexity grows with the project time and it is needed to test the interaction between the process model and the referenced implementation. Its a good practice to use mocks during testing to remove dependencies from frameworks like Camunda BPM or service calls to systems, which are not available or in turn have a lot of dependencies. Camunda BPM has a out-of-the-box mechanism,which is described later in this article. NovaTec developed a Bean Testing framework for full CDI Bean testing without an Java EE application server which is successfully used in many projects.
This article describes how it is possible to use Bean Testing for loading beans and mocks in the context of CDI implementations. This approach doesn’t aim for unit test implementation. It is kind of a component test without the service calls. They will be run during the integration tests. A great side effect: You can use and test with the full CDI API, like injections of interceptors and other mechanisms. This article will compare these two concepts.
Scenario
Most of the Camunda BPM based projects are implemented with frameworks like Java EE or Spring. With these frameworks it is possible to switch between mocks for service implementation and the real implementation with an alternate bean declaration. This test setup will not run with plain classes but with beans of Spring or CDI / Enterprise Java Beans (EJB). This article will describe only the testing possibilities with Camunda BPM process models. For more information about testing in Camunda BPM you can read the following guide: https://docs.camunda.org/manual/latest/user-guide/testing/
The following BPMN process shows the testing scenario for the implemented test examples on GitHub (https://github.com/eberhardheber/camunda-examples). This is a simple pizza ordering process with one branch for canceling the order.
This process contains three human tasks and two service tasks. Before every human task the engine integrates a save state (https://docs.camunda.org/manual/latest/user-guide/process-engine/transactions-in-processes/#transaction-boundaries). The interaction with the process as participant is asynchronous and the engine has to wait for the task completion. We can trigger the next task with “complete(…)” and the user task in the flow will be executed. The following listing shows the handling of this test case:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 |
@Test @Deployment(resources = "process.bpmn") public void testHappyPath() { ProcessInstance processInstance = processEngineRule.getRuntimeService().startProcessInstanceByKey(PROCESS_DEFINITION_KEY); assertThat(processInstance).isStarted().isWaitingAt("UserTask_OrderPizza"); // complete the order task complete(task(), withVariables("pizzaType", "Funghi", "customerName", "John Doe")); assertThat(processInstance).isWaitingAt("UserTask_ApproveOrder"); complete(task(), withVariables("approved", true)); assertThat(processInstance).isWaitingAt("UserTask_DeliverOrder"); complete(task()); assertThat(processInstance).isEnded(); } |
The statement “complete(…)” handles the human tasks and integrates the relevant data for continuing the process flow. When testing a process you should never implement logic nor service calls into the delegates, because if you do so, it is very hard to mock not needed/wanted classes without using Java Reflections or a special framework.
Typical Implementations
Camunda BPM applications are typically implemented in the shared server concept. There it is possible to run the CDI, Spring or plain Java classes as references in the task implementations. Most of the Camunda BPM applications are based on CDI or Spring beans. (If you consider plain java classes be aware that the classes can not be replaced with other ones, because the link in the process model is hard coded. If you want to provide an alternate implementation you have to add a testing process with the mocked implementation. So this is not a really good idea to provide a plain java implementation.)
Camunda Out-of-the-Box Mocking
Camunda BPM provides an out-of-the-box implementation for providing beans in the defined expressions of the BPMN model. You can add all needed classes to the camunda context with the command “Mocks.register(<expression>, <class instance>);”. But be sure, that you mock all services with a mocking framework like Mockito. The following listing shows the pizza example with the mocked service call:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 |
@InjectMocks LoggerDelegate logger = new LoggerDelegate(); @Mock ServiceCall serviceCall; @Mock ServiceCall2 serviceCall2; @Mock ServiceCall3 serviceCall3; PizzaDeliveryDelegate pizzaDeliveryDelegate = new PizzaDeliveryDelegate(); MyDelegate myDelegate = new MyDelegate (); @Before public void init() { when(serviceCall.callService()).thenReturn("Mock 1 called!"); when(serviceCall2.callService()).thenReturn("Mock 2 called!"); when(serviceCall3.callService()).thenReturn("Mock 3 called!"); // You have to add all mock able methods Mocks.register("loggerDelegate", logger); Mocks.register("pizzaDeliveryDelegate", pizzaDeliveryDelegate); Mocks.register("myDelegate ", myDelegate); // add more Mocks to the test } |
If you have a large process model you need to instantiate all beans, which are used in the “expression” or “delegateExpression” field of the process model. In these mocks you possibly need to mock services or other not wanted logic. The full example is available at this repository: https://github.com/eberhardheber/camunda-examples/tree/master/process-test-cdi-pizza-without-beantesting
Java EE CDI Bean Mocking
With Java EE you have CDI Beans or EJBs in your application. Now you need a possibility to test all your logic at once without replacing anything. For testing the beans in the Java EE environment we use Bean Testing (https://github.com/NovaTecConsulting/BeanTest, http://blog.novatec-gmbh.de/unit-testing-jee-applications-cdi/). This bean testing framework loads your beans during the process tests in a CDI container. The next step is to connect the CDI container to the engine. Therefore you have to implement a mock expression manager (https://github.com/eberhardheber/camunda-examples/blob/master/process-test-cdi-pizza/src/test/java/de/novatec/bpm/example/pizza/MockExpressionManager.java) and you need to connect the expression manager to the engine via the “camunda.cfg.xml” file in your resources folder:
1 2 3 4 5 6 7 8 9 10 11 12 |
<?xml version="1.0" encoding="UTF-8"?> <beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd"> <bean id="processEngineConfiguration" class="org.camunda.bpm.extension.process_test_coverage.junit.rules.ProcessCoverageInMemProcessEngineConfiguration"> <property name="history" value="full" /> <property name="expressionManager"> <bean class="de.novatec.bpm.example.pizza.MockExpressionManager" /> </property> </bean> </beans> |
You have connected the CDI container to the engine and you can start testing. If you have problems with resolving the beans, be sure that you have placed the “beans.xml” file in the “src/main/resources/META-INF/” folder. Otherwise the CDI container cannot find the bean classes. For mocking your beans, it is needed that you configure CDI alternatives in the “beans.xml” file in your test directory “src/test/resources/META-INF/”:
1 2 3 4 5 6 7 8 |
<?xml version="1.0"?> <beans xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns="http://java.sun.com/xml/ns/javaee" xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://jboss.org/schema/cdi/beans_1_0.xsd"> <alternatives> <class>de.novatec.bpm.example.pizza.ServiceCallMockProducer</class> </alternatives> </beans> |
The full example is available in the following GitHub repository: https://github.com/eberhardheber/camunda-examples/tree/master/process-test-cdi-pizza
Conclusion
With this approach you can decide: Insert your mocks and logic via the Camunda BPM mechanism “Mocks.register(“sampleService”, new MySampleService());” or you use this Bean Testing approach to insert your implementation into the Camunda BPM context. If you want to register your mocks and logic manually you have to make sure that you do not forget any. Also, you need to instantiate all beans and mock all not wanted/needed classes. In my opinion, it is easier to use the approach with Bean Testing, because you create a link to Camunda BPM once and insert your mocks in your testing “beans.xml”. Everything else will be done by the CDI container. You can also use the full functionality like interceptors in your tests like it is implemented in the Bean Testing example.
If you want to see how your newly written bean testing of camunda process application progresses, you can show the process test coverage in a html file with the “camunda-bpm-process-test-coverage” extension (see here: https://github.com/camunda/camunda-bpm-process-test-coverage). It will show the complete process flow, with the green colored pass through nodes.
Comment article
Recent posts






Comments
Eberhard Heber
Hello Simon,
thank you for the hints! It completes the article. The example in the post should use the full CDI API and needle4j supports only dependency injection. You cannot use transactions and interceptors within the test. If you don’t need these features, your described possibilities with camunda-bpm-needle and camunda-bpm-mockito are really good choices. I will take them into account in my next CamundaBPM projects.
Kind Regards,
Eberhard
Simon Zambrovski
Dear Eberhard,
you are right in your approach for testing without mocking, but please consider that there are ready-to-use Camunda extensions for this purposes. Firstly, https://github.com/camunda/camunda-bpm-needle extension is allowing to specify what components are mocked and which one are used. In addition, https://github.com/camunda/camunda-bpm-mockito extension pimps the default Mockito usage by abaility to mock everything referenced from the BPMN file.
If you are interested in testing of Camunda processes, please alos have a look on our older post from 2013:
https://blog.holisticon.de/2013/12/testgetriebene-geschaeftsprozessmodellierung-camunda-community-meeting-bei-holisticon/
It is five years old, but we still use all of this methods in customer projects.
Kind regards,
Simon