| id | configuration |
|---|---|
| title | Configuration |
| description | See how to configure Camunda Process Test |
import Tabs from "@theme/Tabs"; import TabItem from "@theme/TabItem";
By default, CPT uses a runtime based on Testcontainers. You can customize the runtime to your needs, or replace it with a Remote runtime, for example, if you can't install a Docker runtime.
Configuration is provided through application.yml (or application.properties) when using the Camunda Spring Boot
Starter, or through a camunda-container-runtime.properties file when using the Java client.
:::tip Environment variable resolution (Java client)
When using the Java client, properties in camunda-container-runtime.properties support automatic environment variable resolution. If a property is not explicitly set, it is resolved from an environment variable. The variable name is derived by prepending the CAMUNDA_PROCESSTEST_ prefix, replacing dots with underscores, removing hyphens, and converting everything to uppercase.
For example, judge.chatModel.apiKey resolves to CAMUNDA_PROCESSTEST_JUDGE_CHATMODEL_APIKEY.
:::
The default runtime of CPT is based on Testcontainers. It uses the Camunda Docker image and includes the following components:
- Camunda
- Connectors
:::note Why Testcontainers? CPT follows a common practice by using Testcontainers to provide an isolated, reproducible, and easily configurable environment using Docker containers. This ensures consistent test results, simplifies setup across different platforms, and allows integration with Camunda and other components without manual installation or complex dependencies. :::
:::tip Shared runtime If you use the same runtime configuration for all test classes, then you can use a shared runtime to speed up the test execution. :::
- A Docker-API compatible container runtime, such as Docker on Linux or Docker Desktop on Mac and Windows. If you're experiencing issues with your Docker runtime, have a look at the Testcontainers documentation.
By default, the runtime uses the same version of the Camunda Docker images as the Maven module. You can change the Docker images and other runtime properties in the following way.
<Tabs groupId="client" defaultValue="spring-sdk" queryString values={[ {label: 'Camunda Spring Boot Starter', value: 'spring-sdk' }, {label: 'Java client', value: 'java-client' } ]}>
In your application.yml (or application.properties):
camunda:
process-test:
# Change the version of the Camunda Docker image
camunda-docker-image-version: 8.8.0
# Change the Camunda Docker image
camunda-docker-image-name: camunda/camunda
# Set additional Camunda environment variables
camunda-env-vars:
env_1: value_1
# Expose additional Camunda ports
camunda-exposed-ports:
- 9000
# Change the Camunda logger name
camunda-logger-name: tc.camunda
# Enable Connectors
connectors-enabled: true
# Change the Connectors Docker image
connectors-docker-image-name: camunda/connectors
# Change version of the Connectors Docker image
connectors-docker-image-version: 8.8.0
# Set additional Connectors environment variables
connectors-env-vars:
env_1: value_1
# Set Connectors secrets
connectors-secrets:
secret_1: value_1
# Expose additional Connectors ports
connectors-exposed-ports:
- 9010
# Change the Connectors logger name
connectors-logger-name: tc.connectorsIn your /camunda-container-runtime.properties file:
# Change the version of the Camunda Docker image
camundaDockerImageVersion=8.8.0
# Change the Camunda Docker image
camundaDockerImageName=camunda/camunda
# Set additional Camunda environment variables
camundaEnvVars.env_1=value_1
camundaEnvVars.env_2=value_2
# Expose additional Camunda ports
camundaExposedPorts[0]=9000
camundaExposedPorts[1]=9001
# Change the Camunda logger name
camundaLoggerName=tc.camunda
# Enable Connectors
connectorsEnabled=true
# Change version of the Connectors Docker image
connectorsDockerImageVersion=8.8.0
# Change the Connectors Docker image
connectorsDockerImageName=camunda/connectors
# Set additional Connectors environment variables
connectorsEnvVars.env_1=value_1
connectorsEnvVars.env_2=value_2
# Set Connectors secrets
connectorsSecrets.secret_1=value_1
connectorsSecrets.secret_2=value_2
# Expose additional Connectors ports
connectorsExposedPorts[0]=9010
connectorsExposedPorts[1]=9011
# Change the Connectors logger name
connectorsLoggerName=tc.connectorsAlternatively, you can register the JUnit extension manually and use the fluent builder:
package com.example;
import io.camunda.process.test.api.CamundaProcessTestExtension;
import org.junit.jupiter.api.extension.RegisterExtension;
// No annotation: @CamundaProcessTest
public class MyProcessTest {
@RegisterExtension
private static final CamundaProcessTestExtension EXTENSION =
new CamundaProcessTestExtension()
// Change the version of the Camunda Docker image
.withCamundaDockerImageVersion("8.8.0")
// Change the Camunda Docker image
.withCamundaDockerImageName("camunda/camunda")
// Set additional Camunda environment variables
.withCamundaEnv("env_1", "value_1")
// Expose additional Camunda ports
.withCamundaExposedPort(4567)
// Enable Connectors
.withConnectorsEnabled(true)
// Change the Connectors Docker image
.withConnectorsDockerImageName("camunda/connectors")
// Change version of the Connectors Docker image
.withConnectorsDockerImageVersion("8.8.0")
// Set additional Connectors environment variables
.withConnectorsEnv("env_1", "value_1")
// Set Connectors secrets
.withConnectorsSecret("secret_1", "value_1");
}By default, CPT creates a new runtime for each test class. You can change this behavior and use a shared Testcontainers runtime for all test classes to speed up the test execution. You can enable the shared runtime in the following way.
<Tabs groupId="client" defaultValue="spring-sdk" queryString values={[ {label: 'Camunda Spring Boot Starter', value: 'spring-sdk' }, {label: 'Java client', value: 'java-client' } ]}>
In your application.yml (or application.properties):
camunda:
process-test:
# Switch from a managed to a shared runtime
runtime-mode: sharedAll test classes using the shared runtime will use the same runtime configuration. You can't change the runtime configuration for individual test classes, such as enabling connectors or setting connector secrets. However, you can switch to a managed runtime for individual test classes and override the runtime configuration.
@SpringBootTest(
properties = {
// Use a managed runtime for a different configuration
"camunda.process-test.runtime-mode=managed",
"camunda.process-test.connectors-enabled=true",
}
)
@CamundaSpringProcessTest
public class MyProcessTest {
//
}In your /camunda-container-runtime.properties file:
# Switch from a managed to a shared runtime
runtimeMode=sharedAll test classes using the shared runtime will use the same runtime configuration. You can't change the runtime configuration for individual test classes, such as enabling connectors or setting connector secrets. However, you can switch to a managed runtime for individual test classes and override the runtime configuration.
package com.example;
import io.camunda.process.test.api.CamundaProcessTestExtension;
import org.junit.jupiter.api.extension.RegisterExtension;
// No annotation: @CamundaProcessTest
public class MyProcessTest {
@RegisterExtension
private static final CamundaProcessTestExtension EXTENSION =
new CamundaProcessTestExtension()
// Use a managed runtime for a different configuration
.withRuntimeMode(CamundaProcessTestRuntimeMode.MANAGED)
.withConnectorsEnabled(true);
}Multi-tenancy is disabled by default. You can enable multi-tenancy in the following way:
<Tabs groupId="client" defaultValue="spring-sdk" queryString values={[ {label: 'Camunda Spring Boot Starter', value: 'spring-sdk' }, {label: 'Java client', value: 'java-client' } ]}>
In your application.yml (or application.properties):
camunda:
process-test:
# Enable multi-tenancy
multi-tenancy-enabled: trueBy enabling multi-tenancy, the runtime enables Basic Auth security and creates a default user with username/password
demo with admin rights to interact with the runtime.
A process test using multi-tenancy could look like the following example:
@SpringBootTest
@CamundaSpringProcessTest
public class MyProcessTest {
private static final String DEFAULT_USERNAME = "demo";
private static final String TENANT_ID_1 = "tenant-1";
private static final String TENANT_ID_2 = "tenant-2";
@Autowired
private CamundaClient client;
@Autowired
private CamundaProcessTestContext processTestContext;
private CamundaClient clientForTenant1;
@BeforeEach
void setupTenants() {
// create tenants
client.newCreateTenantCommand().tenantId(TENANT_ID_1).name(TENANT_ID_1).send().join();
client.newCreateTenantCommand().tenantId(TENANT_ID_2).name(TENANT_ID_2).send().join();
// assign the default user to the tenants
client
.newAssignUserToTenantCommand()
.username(DEFAULT_USERNAME)
.tenantId(TENANT_ID_1)
.send()
.join();
client
.newAssignUserToTenantCommand()
.username(DEFAULT_USERNAME)
.tenantId(TENANT_ID_2)
.send()
.join();
// create a client for tenant 1
clientForTenant1 =
processTestContext.createClient(
clientBuilder -> clientBuilder.defaultTenantId(TENANT_ID_1));
}
@Test
void createProcessInstance() {
// given
clientForTenant1
.newDeployResourceCommand()
.addResourceFromClasspath("bpmn/order-process.bpmn")
.send()
.join();
// when
final var processInstance =
clientForTenant1
.newCreateInstanceCommand()
.bpmnProcessId("order-process")
.latestVersion()
.variable("order_id", "order-1")
.send()
.join();
// then
assertThatProcessInstance(processInstance).isCreated();
Assertions.assertThat(processInstance.getTenantId()).isEqualTo(TENANT_ID_1);
}
}In your /camunda-container-runtime.properties file:
# Enable multi-tenancy
multiTenancyEnabled=trueAlternatively, you can register the JUnit extension manually and use the fluent builder:
package com.example;
import io.camunda.process.test.api.CamundaProcessTestExtension;
import org.junit.jupiter.api.extension.RegisterExtension;
// No annotation: @CamundaProcessTest
public class MyProcessTest {
@RegisterExtension
private static final CamundaProcessTestExtension EXTENSION =
new CamundaProcessTestExtension()
// Enable multi-tenancy
.withMultiTenancyEnabled(true);
}By enabling multi-tenancy, the runtime enables Basic Auth security and creates a default user with username/password
demo with admin rights to interact with the runtime.
A process test using multi-tenancy could look like the following example:
@CamundaProcessTest
public class MyProcessTest {
private static final String DEFAULT_USERNAME = "demo";
private static final String TENANT_ID_1 = "tenant-1";
private static final String TENANT_ID_2 = "tenant-2";
private CamundaClient client;
private CamundaProcessTestContext processTestContext;
private CamundaClient clientForTenant1;
@BeforeEach
void setupTenants() {
// create tenants
client.newCreateTenantCommand().tenantId(TENANT_ID_1).name(TENANT_ID_1).send().join();
client.newCreateTenantCommand().tenantId(TENANT_ID_2).name(TENANT_ID_2).send().join();
// assign the default user to the tenants
client
.newAssignUserToTenantCommand()
.username(DEFAULT_USERNAME)
.tenantId(TENANT_ID_1)
.send()
.join();
client
.newAssignUserToTenantCommand()
.username(DEFAULT_USERNAME)
.tenantId(TENANT_ID_2)
.send()
.join();
// create a client for tenant 1
clientForTenant1 =
processTestContext.createClient(
clientBuilder -> clientBuilder.defaultTenantId(TENANT_ID_1));
}
@Test
void createProcessInstance() {
// given
clientForTenant1
.newDeployResourceCommand()
.addResourceFromClasspath("bpmn/order-process.bpmn")
.send()
.join();
// when
final var processInstance =
clientForTenant1
.newCreateInstanceCommand()
.bpmnProcessId("order-process")
.latestVersion()
.variable("order_id", "order-1")
.send()
.join();
// then
assertThatProcessInstance(processInstance).isCreated();
Assertions.assertThat(processInstance.getTenantId()).isEqualTo(TENANT_ID_1);
}
}:::info
You should assign the default user (demo) to all tenants to ensure that the assertions can access all data.
:::
You can add custom containers to the managed or shared Testcontainers runtime, for example, to add a database, an MCP server, or a mock service.
The CPT runtime manages the lifecycle of the custom containers and ensures that they are started before the tests and stopped after the tests. The custom containers are added to the same network as the Camunda and Connectors containers to allow communication between the containers.
You can add a custom container in the following way.
<Tabs groupId="client" defaultValue="spring-sdk" queryString values={[ {label: 'Camunda Spring Boot Starter', value: 'spring-sdk' }, {label: 'Java client', value: 'java-client' } ]}>
Implement a CamundaProcessTestContainerProvider bean that creates the custom container.
In this example, we create a WireMock container to mock external HTTP calls in the tests.
@Configuration
public class TestConfig {
@Bean
public CamundaProcessTestContainerProvider wireMockProvider() {
return containerContext -> new WireMockContainer();
}
// A WireMock container to mock external HTTP calls in the tests
private static final class WireMockContainer extends GenericContainer<WireMockContainer> {
public WireMockContainer() {
// Configure the Docker image
super("wiremock/wiremock:3.13.0");
// Configure the network alias for communication between the containers
withNetworkAliases("wiremock");
// Configure the ports to expose
withExposedPorts(8080);
// Configure the logger
withLogConsumer(new Slf4jLogConsumer(LoggerFactory.getLogger("tc.wiremock"), true));
// Configure the wait strategy to ensure that the container is ready before running the tests
waitingFor(
Wait.forHttp("/__admin/mappings").forPort(8080).withMethod("GET").forStatusCode(200));
// Custom container-specific configuration
withCopyFileToContainer(
// Copy the WireMock mapping file for the HTTP stubs to the container
MountableFile.forClasspathResource("/wiremock/mapping.json"),
"/home/wiremock/mappings/mapping.json");
}
}
}In the application.yml configuration, we use a connector secret to bind the connector task to the WireMock container
using its network alias wiremock and the exposed port 8080.
camunda:
process-test:
connectors-enabled: true
connectors-secrets:
BASE_URL: http://wiremock:8080Implement the CamundaProcessTestContainerProvider interface that creates the custom container.
In this example, we create a WireMock container to mock external HTTP calls in the tests.
public class WireMockContainerProvider implements CamundaProcessTestContainerProvider {
@Override
public GenericContainer<?> createContainer(final CamundaProcessTestContainerContext containerContext) {
return new WireMockContainer();
}
// A WireMock container to mock external HTTP calls in the tests
private static final class WireMockContainer extends GenericContainer<WireMockContainer> {
public WireMockContainer() {
// Configure the Docker image
super("wiremock/wiremock:3.13.0");
// Configure the network alias for communication between the containers
withNetworkAliases("wiremock");
// Configure the ports to expose
withExposedPorts(8080);
// Configure the logger
withLogConsumer(new Slf4jLogConsumer(LoggerFactory.getLogger("tc.wiremock"), true));
// Configure the wait strategy to ensure that the container is ready before running the tests
waitingFor(
Wait.forHttp("/__admin/mappings").forPort(8080).withMethod("GET").forStatusCode(200));
// Custom container-specific configuration
withCopyFileToContainer(
// Copy the WireMock mapping file for the HTTP stubs to the container
MountableFile.forClasspathResource("/wiremock/mapping.json"),
"/home/wiremock/mappings/mapping.json");
}
}
}Register the container provider using the Java ServiceLoader mechanism by creating a file
io.camunda.process.test.api.runtime.CamundaProcessTestContainerProvider in the src/test/resources/META-INF/services
directory of your project and adding the fully qualified name of the container provider implementation:
com.example.WireMockContainerProvider
In the /camunda-container-runtime.properties configuration file, we use a connector secret to bind the connector task
to the WireMock container using its network alias wiremock and the exposed port 8080.
connectorsEnabled=true
connectorsSecrets.BASE_URL=http://wiremock:8080Alternatively, you can register the container provider on the JUnit extension using the fluent builder:
// No annotation: @CamundaProcessTest
public class MyProcessTest {
@RegisterExtension
private static final CamundaProcessTestExtension EXTENSION =
new CamundaProcessTestExtension()
.withContainerProvider(new WireMockContainerProvider())
.withConnectorsEnabled(true)
.withConnectorsSecret("BASE_URL", "http://wiremock:8080");
}Instead of using the managed Testcontainers runtime, you can configure CPT to connect to a remote runtime, for example, to a local Camunda 8 Run running on your machine.
When to use it:
- You can't install a Docker-API compatible container runtime
- Debugging of test case on your local machine
:::info You are responsible for configuring and managing the remote runtime. Ensure the runtime is running before executing tests. Keep in mind that CPT automatically deletes all data between test runs to maintain a clean state. :::
- Install a Camunda 8 runtime, for example, Camunda 8 Run
- Expose the management API port (
9600) to delete the data between test runs (by default for a local Camunda 8 Run) - Enable the management clock endpoint to allow clock manipulations
You can configure Camunda 8 Run by
defining a application.yaml file with:
zeebe.clock.controlled: trueBy default, Camunda 8 Run loads the application.yaml from the distribution's root directory. If you use a different
path, then you need to set the path when starting the application with the command line argument
--config=application.yaml:
./start.sh --config=application.yaml
You need to set the following property to switch to a remote runtime.
<Tabs groupId="client" defaultValue="spring-sdk" queryString values={[ {label: 'Camunda Spring Boot Starter', value: 'spring-sdk' }, {label: 'Java client', value: 'java-client' } ]}>
In your application.yml (or application.properties):
camunda:
process-test:
# Switch from a managed to a remote runtime
runtime-mode: remoteIn your /camunda-container-runtime.properties file:
# Switch from a managed to a remote runtime
runtimeMode=remoteAlternatively, you can register the JUnit extension manually and use the fluent builder:
package com.example;
import io.camunda.process.test.api.CamundaProcessTestExtension;
import org.junit.jupiter.api.extension.RegisterExtension;
// No annotation: @CamundaProcessTest
public class MyProcessTest {
@RegisterExtension
private static final CamundaProcessTestExtension EXTENSION =
new CamundaProcessTestExtension()
// Switch from a managed to a remote runtime
.withRuntimeMode(CamundaProcessTestRuntimeMode.REMOTE);
}By default, CPT connects to a remote Camunda 8 Run running on your local machine. CPT checks if the remote runtime is available and ready, before running the tests. It waits up to 1 minute for the remote runtime to become ready.
You can change the connection to the remote runtime and the connection time in the following way.
<Tabs groupId="client" defaultValue="spring-sdk" queryString values={[ {label: 'Camunda Spring Boot Starter', value: 'spring-sdk' }, {label: 'Java client', value: 'java-client' } ]}>
In your application.yml (or application.properties):
camunda:
process-test:
runtime-mode: remote
# Change the connection (default: Camunda 8 Run)
remote:
camunda-monitoring-api-address: http://0.0.0.0:9600
connectors-rest-api-address: http://0.0.0.0:8085
# The connection timeout in ISO-8601 duration format (default: PT1M)
runtime-connection-timeout: PT1M
client:
rest-address: http://0.0.0.0:8080
grpc-address: http://0.0.0.0:26500In your /camunda-container-runtime.properties file:
runtimeMode=remote
# Change the connection (default: Camunda 8 Run)
remote.camundaMonitoringApiAddress=http://0.0.0.0:9600
remote.connectorsRestApiAddress=http://0.0.0.0:8085
remote.client.grpcAddress=http://0.0.0.0:26500
remote.client.restAddress=http://0.0.0.0:8080
# The connection timeout in ISO-8601 duration format (default: PT1M)
remote.runtimeConnectionTimeout=PT1MAlternatively, register the JUnit extension manually and use the fluent builder:
package com.example;
import io.camunda.process.test.api.CamundaProcessTestExtension;
import org.junit.jupiter.api.extension.RegisterExtension;
// No annotation: @CamundaProcessTest
public class MyProcessTest {
@RegisterExtension
private static final CamundaProcessTestExtension EXTENSION =
new CamundaProcessTestExtension()
.withRuntimeMode(CamundaProcessTestRuntimeMode.REMOTE)
// Change the connection (default: Camunda 8 Run)
.withRemoteCamundaClientBuilderFactory(() -> CamundaClient.newClientBuilder()
.restAddress(URI.create("http://0.0.0.0:8080"))
.grpcAddress(URI.create("http://0.0.0.0:26500"))
)
.withRemoteCamundaMonitoringApiAddress(URI.create("http://0.0.0.0:9600"))
.withRemoteConnectorsRestApiAddress(URI.create("http://0.0.0.0:8085"))
// Change the connection timeout (default: PT1M)
.withRemoteRuntimeConnectionTimeout(Duration.ofMinutes(1));
}You can use a remote runtime to debug your test cases on your local machine. Set breakpoints in your test case and run the test in debug mode from your IDE.
When the test execution stops at a breakpoint, you can inspect the process instance state using Operate and the user task state using Tasklist. You can also use the Camunda client to interact with the runtime from the debugger console.
CPT generates an HTML and JSON coverage report of your BPMN processes. You can configure the report generation in the following way.
<Tabs groupId="client" defaultValue="spring-sdk" queryString values={[ {label: 'Camunda Spring Boot Starter', value: 'spring-sdk' }, {label: 'Java client', value: 'java-client' } ]}>
In your application.yml (or application.properties):
camunda:
process-test:
coverage:
# Change the directory where the report is generated
reportDirectory: target/coverage-report
# Exclude processes from the report
excludedProcesses:
- process_1
- process_2In your /camunda-container-runtime.properties file:
# Change the directory where the report is generated
coverage.reportDirectory=target/coverage-report
# Exclude processes from the report
excludedProcesses[0]=process_1
excludedProcesses[1]=process_2The test runtime uses SLF4J as the logging framework. If needed, you can enable the logging for the following packages:
io.camunda.process.test- The test runtime (recommended levelinfo)tc.camunda- The Camunda Docker container (recommended levelerror)tc.connectors- The connectors Docker container (recommended levelerror)org.testcontainers- The Testcontainers framework (recommended levelwarn)
Judge assertions use a configured LLM to score process variables against natural language expectations. This section covers how to set up the LLM provider and tune the judge behavior.
CPT provides an optional LangChain4j integration module that ships with preconfigured
support for major LLM providers: OpenAI, Anthropic, Amazon Bedrock, Azure OpenAI, and OpenAI-compatible APIs.
LangChain4j requires Java 17+. You can provide your own LLM integration through a
custom ChatModelAdapter instead (see Custom ChatModelAdapter).
:::tip For a guided walkthrough of setting up and testing agentic processes, see Testing agentic processes. :::
<Tabs groupId="client" defaultValue="spring-sdk" queryString values={[ {label: 'Camunda Spring Boot Starter', value: 'spring-sdk' }, {label: 'Java client', value: 'java-client' } ]}>
Camunda Process Test Spring includes the LangChain4j providers as a transitive dependency. No additional dependency is needed.
Add the camunda-process-test-langchain4j dependency to your project:
<dependency>
<groupId>io.camunda</groupId>
<artifactId>camunda-process-test-langchain4j</artifactId>
<scope>test</scope>
</dependency>If you provide a custom ChatModelAdapter (see Custom ChatModelAdapter), this dependency
is not required.
All judge properties are nested under camunda.process-test.judge in Spring configuration. In Java properties files,
use the judge. prefix with camelCase keys (for example, judge.chat-model.api-key becomes judge.chatModel.apiKey).
For configuration examples, see Set up an LLM provider.
Unless noted otherwise, properties in the provider tables are required.
| Property | Type | Default | Description |
|---|---|---|---|
judge.threshold |
double |
0.5 |
Confidence threshold (0.0 to 1.0) for the judge to pass. |
judge.custom-prompt |
string |
Custom evaluation prompt replacing the default criteria. |
<Tabs groupId="provider" defaultValue="openai" queryString values={[ {label: 'OpenAI', value: 'openai' }, {label: 'Anthropic', value: 'anthropic' }, {label: 'Amazon Bedrock', value: 'amazon-bedrock' }, {label: 'Azure OpenAI', value: 'azure-openai' }, {label: 'OpenAI-compatible', value: 'openai-compatible' }, {label: 'Custom/SPI', value: 'custom' } ]}>
| Property | Required | Type | Description |
|---|---|---|---|
judge.chat-model.provider |
Yes | string |
Set to openai. |
judge.chat-model.model |
Yes | string |
Model name (for example gpt-4o). |
judge.chat-model.api-key |
Yes | string |
API key. |
judge.chat-model.timeout |
No | duration |
Request timeout (ISO-8601 duration, for example PT30S). |
judge.chat-model.temperature |
No | double |
Temperature for response randomness (0.0 to 2.0). |
| Property | Required | Type | Description |
|---|---|---|---|
judge.chat-model.provider |
Yes | string |
Set to anthropic. |
judge.chat-model.model |
Yes | string |
Model name (for example claude-sonnet-4-20250514). |
judge.chat-model.api-key |
Yes | string |
API key. |
judge.chat-model.timeout |
No | duration |
Request timeout (ISO-8601 duration, for example PT30S). |
judge.chat-model.temperature |
No | double |
Temperature for response randomness (0.0 to 2.0). |
Supports Bedrock long-term API keys or AWS IAM credentials. Falls back to the AWS default credentials provider chain.
| Property | Required | Type | Description |
|---|---|---|---|
judge.chat-model.provider |
Yes | string |
Set to amazon-bedrock. |
judge.chat-model.model |
Yes | string |
Model name (for example eu.anthropic.claude-haiku-4-5-20251001-v1:0). |
judge.chat-model.region |
No | string |
AWS region (for example eu-central-1). |
judge.chat-model.api-key |
No | string |
Bedrock long-term API key. Optional if using IAM credentials or the default credentials chain. |
judge.chat-model.credentials.access-key |
Conditionally, with secret key | string |
AWS IAM access key. Optional if using an API key or the default credentials chain. |
judge.chat-model.credentials.secret-key |
Conditionally, with access key | string |
AWS IAM secret key. Optional if using an API key or the default credentials chain. |
judge.chat-model.timeout |
No | duration |
Request timeout (ISO-8601 duration, for example PT30S). |
judge.chat-model.temperature |
No | double |
Temperature for response randomness (0.0 to 2.0). |
Supports API key authentication. Falls back to
DefaultAzureCredential.
| Property | Required | Type | Description |
|---|---|---|---|
judge.chat-model.provider |
Yes | string |
Set to azure-openai. |
judge.chat-model.model |
Yes | string |
Azure deployment name. |
judge.chat-model.endpoint |
Yes | string |
Azure OpenAI resource URL (for example https://my-resource.openai.azure.com/). |
judge.chat-model.api-key |
No | string |
API key. Optional; if omitted, falls back to DefaultAzureCredential. |
judge.chat-model.timeout |
No | duration |
Request timeout (ISO-8601 duration, for example PT30S). |
judge.chat-model.temperature |
No | double |
Temperature for response randomness (0.0 to 2.0). |
For local models (such as Ollama) or any third-party API that implements the OpenAI chat completions format.
| Property | Required | Type | Description |
|---|---|---|---|
judge.chat-model.provider |
Yes | string |
Set to openai-compatible. |
judge.chat-model.model |
Yes | string |
Model name (for example llama3). |
judge.chat-model.base-url |
Yes | string |
Base URL for the API endpoint (for example http://localhost:11434/v1). |
judge.chat-model.api-key |
No | string |
API key. Optional for local providers. |
judge.chat-model.headers.* |
No | map |
Custom HTTP headers. |
judge.chat-model.timeout |
No | duration |
Request timeout (ISO-8601 duration, for example PT30S). |
judge.chat-model.temperature |
No | double |
Temperature for response randomness (0.0 to 2.0). |
For providers not listed above, use a custom provider name and pass arbitrary properties. See Custom ChatModelAdapter for implementation details.
| Property | Required | Type | Description |
|---|---|---|---|
judge.chat-model.provider |
Yes | string |
Custom provider name matching your SPI implementation. |
judge.chat-model.model |
Yes | string |
Model name. |
judge.chat-model.custom-properties.* |
No | map |
Arbitrary key-value pairs passed to SPI providers via ProviderConfig.getCustomProperties(). |
judge.chat-model.timeout |
No | duration |
Request timeout (ISO-8601 duration, for example PT30S). |
judge.chat-model.temperature |
No | double |
Temperature for response randomness (0.0 to 2.0). |
You can replace the default evaluation criteria with a custom prompt. The custom prompt replaces only the evaluation criteria (the "You are an impartial judge..." preamble). The system still controls the expectation and value injection, the scoring rubric, and the JSON output format.
By default, CPT uses an internal prompt that instructs the model to act as an impartial judge, compare the provided value against the natural language expectation, apply the documented scoring rubric, and return the result in the expected JSON structure.
<Tabs groupId="client" defaultValue="spring-sdk" queryString values={[ {label: 'Camunda Spring Boot Starter', value: 'spring-sdk' }, {label: 'Java client', value: 'java-client' } ]}>
camunda:
process-test:
judge:
custom-prompt: "You are a domain expert evaluating financial data accuracy."judge.customPrompt=You are a domain expert evaluating financial data accuracy.Or programmatically:
JudgeConfig.of(prompt -> myChatModelAdapter.generate(prompt))
.withCustomPrompt("You are a domain expert evaluating financial data accuracy.");You can also override the custom prompt for a single assertion chain:
assertThat(processInstance)
.withJudgeConfig(config -> config
.withCustomPrompt("You are a domain expert evaluating financial data accuracy."))
.hasVariableSatisfiesJudge("result", "Contains valid totals.");You can provide your own ChatModelAdapter implementation without depending on the camunda-process-test-langchain4j
module. A ChatModelAdapter is a functional interface that takes a prompt string and returns a response string.
<Tabs groupId="client" defaultValue="spring-sdk" queryString values={[ {label: 'Camunda Spring Boot Starter', value: 'spring-sdk' }, {label: 'Java client', value: 'java-client' } ]}>
If you have a single ChatModelAdapter bean and no provider property is set, CPT auto-detects and uses it:
@TestConfiguration
class JudgeTestConfig {
@Bean
ChatModelAdapter chatModelAdapter() {
return prompt -> myChatModelAdapter.generate(prompt);
}
}When you have multiple beans, set provider to the bean name you want to use. In Spring, the bean name defaults to
the method name:
@TestConfiguration
class JudgeTestConfig {
@Bean
ChatModelAdapter openAiAdapter() { /* ... */ }
@Bean
ChatModelAdapter ollamaAdapter() { /* ... */ }
}camunda:
process-test:
judge:
chat-model:
provider: "ollamaAdapter" # matches the bean method name:::note Resolution order
When using @CamundaSpringProcessTest, CPT resolves the judge adapter in the following order:
- If a single
ChatModelAdapterbean exists and noproviderproperty is configured, that bean is used automatically. - If the
providerproperty is configured and a bean with a matching name exists, that bean is selected. - If no matching bean is found, CPT falls back to the built-in LangChain4j implementations, provided that
camunda-process-test-langchain4jis on the classpath. - If a
provideris configured but no matching implementation can be resolved at all, CPT throws an exception.
:::
Alternatively, you can configure the judge programmatically. Set the configuration globally
using CamundaAssert.setJudgeConfig():
CamundaAssert.setJudgeConfig(
JudgeConfig.of(prompt -> myChatModelAdapter.generate(prompt))
.withThreshold(0.8));Implement ChatModelAdapterProvider and register it through META-INF/services:
public class MyCustomProvider implements ChatModelAdapterProvider {
@Override
public String getProviderName() {
return "my-provider";
}
@Override
public ChatModelAdapter create(ProviderConfig config) {
String endpoint = config.getCustomProperties().get("endpoint");
return prompt -> callEndpoint(endpoint, prompt);
}
}Register the provider in META-INF/services/io.camunda.process.test.api.judge.ChatModelAdapterProvider:
com.example.MyCustomProvider
Alternatively, you can configure the judge programmatically. Set the configuration globally
using CamundaAssert.setJudgeConfig():
CamundaAssert.setJudgeConfig(
JudgeConfig.of(prompt -> myChatModelAdapter.generate(prompt))
.withThreshold(0.8));Or register the JUnit extension manually with a judge configuration:
@RegisterExtension
CamundaProcessTestExtension extension = new CamundaProcessTestExtension()
.withJudgeConfig(JudgeConfig.of(prompt -> myChatModelAdapter.generate(prompt))
.withThreshold(0.8));