Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
84 changes: 84 additions & 0 deletions one-time-examples/java-camunda-test-containers/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,84 @@
# Camunda 7 Unit Testing in Java with TestContainers and Service Mocking

## 📌 Overview
This project demonstrates **how to write unit tests for Camunda BPMN processes in Java** using JUnit, Mockiito and TestContainers. It sets up a testable Camunda environment, mocks external dependencies, and validates process execution.

## 🚀 Getting Started

### **1. Prerequisites**
Ensure you have the following installed:
- Maven
- Docker (for running Camunda in TestContainers)

### **2. Installation and Running Tests**
Clone the repository and install dependencies:

```sh
mvn install

```

This will:
1. Start a **Camunda TestContainer**.
2. Deploy a BPMN process.
3. Execute test cases using mocked dependencies.
4. Validate process execution.
5. Tear down the container after tests complete.

## 🛠 How It Works

### **1. Setting Up Camunda for Tests and Deploying the process**
The `CamundaWorkerTest.java` file starts a **Camunda TestContainer**:

```java
camunda = new GenericContainer<>(DockerImageName.parse("camunda/camunda-bpm-platform:7.22.0"))
.withExposedPorts(8080);
camunda.start();

baseUrl = "http://" + camunda.getHost() + ":" + camunda.getMappedPort(8080) + "/engine-rest";
waitForCamunda();

camundaHelper = new CamundaHelper(baseUrl);

creditServiceMock = Mockito.mock(CreditService.class);
ExternalTaskWorker.start(baseUrl, creditServiceMock);

camundaHelper.deployBpmnDiagram("src/test/resources/java-script-external.bpmn");
...
```

### **2. Writing Unit Tests**
The unit tests are executed thru CamundaWorkerTest which validates Camunda process execution:
```java

when(creditServiceMock.getCreditScore()).thenReturn(80);
when(creditServiceMock.getCustomerExists()).thenReturn(false);

String processKey = "ApplicationReceived";
var processInstance = camundaHelper.startProcessInstance(processKey, Map.of(
"someInput", Map.of("value", "test", "type", "String")
));

sleep(5000);

assertTrue(camundaHelper.checkElement(processInstance.id, "Add Customer", true));
assertTrue(camundaHelper.checkElement(processInstance.id, "Send Confirmation", true));
assertTrue(camundaHelper.instanceIsCompleted(processInstance.id));

verify(creditServiceMock).getCreditScore();
verify(creditServiceMock).getCustomerExists();

```

### **3. Mocking External Dependencies**
The `CreditService.java` file is **mocked in tests**:

```java
creditServiceMock = Mockito.mock(CreditService.class);
```

## 🔥 Key Features
✔️ Uses **Mockito** for unit testing.
✔️ Runs Camunda BPM in a **TestContainer** (Docker).
✔️ Mocks **external dependencies** (e.g., `CreditService`).
✔️ Validates process execution (completed, user tasks, rejections).
84 changes: 84 additions & 0 deletions one-time-examples/java-camunda-test-containers/pom.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,84 @@
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://www.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>com.example</groupId>
<artifactId>java-camunda-test-containers</artifactId>
<version>1.0-SNAPSHOT</version>

<dependencies>
<!-- Camunda External Task Client (Java) -->
<dependency>
<groupId>org.camunda.bpm</groupId>
<artifactId>camunda-external-task-client</artifactId>
<version>7.20.0</version>
</dependency>

<!-- Testcontainers -->
<dependency>
<groupId>org.testcontainers</groupId>
<artifactId>testcontainers</artifactId>
<version>1.19.3</version>
</dependency>

<!-- JUnit 5 -->
<dependency>
<groupId>org.junit.jupiter</groupId>
<artifactId>junit-jupiter</artifactId>
<version>5.10.2</version>
<scope>test</scope>
</dependency>

<!-- Mockito -->
<dependency>
<groupId>org.mockito</groupId>
<artifactId>mockito-core</artifactId>
<version>5.12.0</version>
<scope>test</scope>
</dependency>

<!-- SLF4J for logging -->
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-simple</artifactId>
<version>2.0.9</version>
</dependency>

<dependency>
<groupId>javax.xml.bind</groupId>
<artifactId>jaxb-api</artifactId>
<version>2.3.1</version>
</dependency>
<dependency>
<groupId>org.glassfish.jaxb</groupId>
<artifactId>jaxb-runtime</artifactId>
<version>2.3.1</version>
</dependency>

<dependency>
<groupId>com.squareup.okhttp3</groupId>
<artifactId>okhttp</artifactId>
<version>4.12.0</version>
</dependency>


</dependencies>

<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<version>3.10.1</version>
<configuration>
<source>17</source>
<target>17</target>
</configuration>
</plugin>
<plugin>
<artifactId>maven-surefire-plugin</artifactId>
<version>3.0.0-M5</version>
</plugin>
</plugins>
</build>
</project>
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
package com.example.worker;

import java.util.Random;

public class CreditService {
private final Random random = new Random();

public int getCreditScore() {
return 0 + random.nextInt(100); // Range: 0-100
}

public boolean getCustomerExists() {
return random.nextBoolean();
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,83 @@

package com.example.worker;

import java.util.Map;
import java.util.logging.Logger;

import org.camunda.bpm.client.ExternalTaskClient;

public class ExternalTaskWorker {

private static final Logger LOGGER = Logger.getLogger("ExternalTaskWorker");

public static void start(String baseUrl, CreditService creditService) {
ExternalTaskClient client = ExternalTaskClient
.create()
.baseUrl(baseUrl)
.asyncResponseTimeout(10000)
.build();

client.subscribe("credit-score")
.lockDuration(1000)
.handler((externalTask, externalTaskService) -> {

LOGGER.info("✔ credit-score triggered");

int creditScore = creditService.getCreditScore();
boolean customerExists = creditService.getCustomerExists();

LOGGER.info("✔ Credit Score: " + creditScore);
LOGGER.info("✔ Customer Exists: " + customerExists);

externalTaskService.complete(externalTask,
Map.of("creditScore", creditScore, "customerExists", customerExists));
})
.open();


client.subscribe("add-customer")
.lockDuration(1000)
.handler((externalTask, externalTaskService) -> {

LOGGER.info("✔ add-customer triggered");

boolean customerExists = externalTask.getVariable("customerExists");

LOGGER.info("customerExists" + customerExists);

if (customerExists) {
externalTaskService.handleBpmnError(externalTask, "CustomerExists","customer allready exist");
}else {
externalTaskService.complete(externalTask,
Map.of("messageSend", true));
}



})
.open();

client.subscribe("send-confirmation")
.lockDuration(1000)
.handler((externalTask, externalTaskService) -> {

LOGGER.info("✔ send-confirmation triggered");
externalTaskService.complete(externalTask,
Map.of("messageSend", true));
})
.open();


client.subscribe("send-rejection")
.lockDuration(1000)
.handler((externalTask, externalTaskService) -> {

LOGGER.info("✔ send-rejection triggered");
externalTaskService.complete(externalTask,
Map.of("rejected", true));
})
.open();


}
}
Loading