Testing processes for Camunda Cloud and Zeebe

Jan 11, 2022 Guide

From a technical perspective, your BPMN processes are code. Therefore, process models should be treated and tested like code. Alongside the importance of testing, let’s highlight some advantages of writing unit tests for your processes: 

  1. Facilitate understanding
  2. Accelerate development
  3. Avoid regression by increasing maintainability and stability

We are happy to introduce Zeebe-Process-Test in the 1.3 release, which makes it possible to unit test BPMN processes for Camunda Cloud and Zeebe using Java and JUnit 5. This is comparable to the camunda-bpm-assert library for running unit tests with Camunda Platform. 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 provides you with a set of assertions which can be used to verify the process behavior. Before trying this out, ensure you have checked the 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. It allows you to send commands to the engine.
  • RecordStreamSource – This gives you access to all the records processed by the engine. It is what the assertions use to verify expectations. This grants you the freedom to create your own assertions.

Let’s take a look on how an exemplary test-class using Zeebe-Process-Test could look like:

@ZeebeProcessTest
class DeploymentAssertTest {
  private InMemoryEngine engine;
  private ZeebeClient client;
  private RecordStreamSource recordStreamSource;
}

Awesome! Now we can now 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. 

In our unit tests, we can verify the following: 

  • The deployment succeeds
  • A process instance can be started successfully
  • A proper job is created for the service task, which can be completed as expected
  • The process ends successfully afterwards

Show me code!

Let’s write our first test to check that 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);
	…

Now, let’s also test that 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 completed successfully:

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 are also invited to contribute to the zeebe-process-test project.

Leave a Reply

Your email address will not be published. Required fields are marked *