*Camunda Platform 8, our cloud-native solution for process orchestration, launched in April 2022. Images and supporting documentation in this post may reflect an earlier version of our cloud and software solutions.
From a technical perspective, your BPMN processes are code. Therefore, process models should be treated and tested like code. Along with sharing the importance of testing, let’s also highlight some advantages of writing unit tests for your processes:
- Facilitate understanding
- Accelerate development
- Avoid regression by increasing maintainability and stability
We’re happy to introduce Zeebe-Process-Test in the 1.3.0 release, which makes it possible to unit test BPMN processes for Camunda Platform 8 and Zeebe using Java and JUnit 5. This is comparable to the camunda-bpm-assert library for running unit tests with Camunda Platform 7. Keep in mind that this release is not yet production-ready, and the API is still likely to change.
Zeebe-Process-Test can spin up an in-memory Zeebe workflow engine and provide you with a set of assertions, which can be used to verify the process behavior. Before trying this out, ensure you have the following prerequisites:
- Java 11+
- JUnit 5
Guide: How to Write Your First Test Case
First, you need to set up a common Maven project with Java in your IDE of choice. Ensure you have fulfilled all prerequisites listed above.
Next, add dependencies to the project’s `pom.xml`.
<dependency>
<groupId>io.camunda</groupId>
<artifactId>zeebe-process-test</artifactId>
<version>1.3.0</version>
<scope>test</scope>
</dependency>
If you are using Spring Boot, ensure you remove the @SpringBootTest annotation. To be clear here, we are now going to write a plain Java JUnit test.
Now, we can focus on creating a test class for our process unit tests. Your newly-created class should be annotated with @ZeebeProcessTest. This annotation creates and starts a new in-memory engine for each test case. Furthermore, it optionally interjects three fields in your test class:
- InMemoryEngine – This is the engine that runs your process. It provides some basic functionality to help you write your tests, such as waiting for an idle state and increasing the time.
- ZeebeClient – This is the client that allows you to communicate with the engine and send commands to it.
- RecordStreamSource – This gives you access to all the records processed by the engine. It’s what the assertions use to verify expectations. This grants you the freedom to create your own assertions.
Let’s take a look at how an exemplary test-class using Zeebe-Process-Test should look like:
@ZeebeProcessTest
class DeploymentAssertTest {
private InMemoryEngine engine;
private ZeebeClient client;
private RecordStreamSource recordStreamSource;
}
Now, we can focus on a BPMN process to test. Let’s start with a simple process model like the one below. It contains one start event, one service task, and one end event.
Show me code!
Let’s write our first test to check whether the process model can be deployed to the engine correctly. Make sure to import the @Test annotation from JUnit 5.
(org.junit.jupiter.api.Test) and not JUnit 4 (org.junit.Test)
@Test
public void testSimpleProcess() {
//When
DeploymentEvent deploymentEvent = client.newDeployCommand()
.addResourceFromClasspath("test-process.bpmn")
.send()
.join();
//Then
BpmnAssert.assertThat(deploymentEvent);
…
Moreover, let’s also test whether a process instance can be started successfully and assert that it has passed the start event of the process model correctly:
//When
ProcessInstanceEventevent=client.newCreateInstanceCommand()
.bpmnProcessId("TestProcess")
.latestVersion()
.send()
.join();
//Then
ProcessInstanceAssert processInstanceAssertions = BpmnAssert.assertThat(event);
processInstanceAssertions.hasPassedElement("StartEvent_1");
…
Next, let’s assert that a job was created for the service task, so we can complete it here. As part of this step, you can invoke the real JobHandler code you have, or bypass the real logic for your test case. A common practice is to use your JobHandler, but inject mocks into it.
…
//When
ActivateJobsResponse response= client.newActivateJobsCommand()
.jobType("serviceTask")
.maxJobsToActivate(1)
.send()
.join();
//Then
ActivatedJob activatedJob = response.getJobs().get(0);
BpmnAssert.assertThat(activatedJob);
//TODO: invoke service task logic as required
client.newCompleteCommand(activatedJob.getKey()).send().join();
…
Finally, we assert that the process instances were successfully completed:
instanceAssert.isCompleted();
}
Before checking if the process is completed, we may need to delay the test to get the correct results. This is due to the asynchronous processing within Zeebe and can be done by using engine.waitForIdleState().
Give It a Try and Find Out More
You can find the example outlined in this document in the following GitHub repository. You’re also invited to contribute to the Zeebe-Process-Test project.