diff --git a/CONTRIBUTING.MD b/CONTRIBUTING.MD index 89c520b0143..8f188676981 100644 --- a/CONTRIBUTING.MD +++ b/CONTRIBUTING.MD @@ -153,7 +153,7 @@ the Apache Spark contributor guidelines, and the Kubernetes contributor guide. [GitHub]: https://lab.github.com/ [Contributor License Agreement]: https://cla-assistant.io/camunda-cloud/camunda-cloud-documentation [code style guidelines]: https://github.com/camunda-cloud/zeebe/wiki/Code-Style -[testing best practices]: https://docs.camunda.io/docs/apis-clients/java-client/testing/ +[testing best practices]: https://docs.camunda.io/docs/apis-clients/java-client/zeebe-process-test/ [Conventional Commits]: ./conventional-commits [template and settings files]: https://github.com/camunda/camunda-bpm-platform/tree/master/settings [Technical Writing Style Guide]: https://github.com/camunda-cloud/camunda-cloud-documentation/blob/master/howtos/technical-writing-styleguide.md diff --git a/docs/apis-clients/java-client/index.md b/docs/apis-clients/java-client/index.md index eb0ddb82c69..d3204db178d 100644 --- a/docs/apis-clients/java-client/index.md +++ b/docs/apis-clients/java-client/index.md @@ -92,5 +92,5 @@ ZeebeClient client = - [Getting Started Guide](https://github.com/camunda-cloud/camunda-cloud-get-started): A comprehensive tutorial that covers Camunda Modeler, Operate, and the Java client. - [Job worker](job-worker.md): An introduction to the Java client's job worker. - [Logging](logging.md): An introduction to configuring logging for a Zeebe client. -- [Writing tests](testing.md): An introduction to writing tests that use an embedded version of the workflow engine. +- [Writing tests](zeebe-process-test.md): An introduction to unit testing processes. - [Examples](/apis-clients/java-client-examples/index.md): A collection of specific examples for different use cases. diff --git a/docs/apis-clients/java-client/testing.md b/docs/apis-clients/java-client/testing.md deleted file mode 100644 index d809839b56c..00000000000 --- a/docs/apis-clients/java-client/testing.md +++ /dev/null @@ -1,74 +0,0 @@ ---- -id: testing -title: "Writing tests" -description: "Use the zeebe-test module to write JUnit tests for your job worker and BPMN process." ---- - -You can use the `zeebe-test` module to write JUnit tests for your job worker and BPMN process. This provides a JUnit rule to bootstrap the broker and some basic assertions. - -:::note -`zeebe-test` is [deprecated for removal](./reference/announcements.md). -::: - -## Usage in a Maven project - -Add `zeebe-test` as a Maven test dependency to your project: - -```xml - - io.camunda - zeebe-test - test - -``` - -## Bootstrap the broker - -Use the `ZeebeTestRule` in your test case to start an embedded broker. This contains a client which can be used to deploy a BPMN process or create an instance. - -```java -import io.camunda.zeebe.client.ZeebeClient; -import io.camunda.zeebe.client.api.response.ProcessInstanceEvent; -import org.junit.Before; -import org.junit.Rule; -import org.junit.Test; - -public class MyTest { - - @Rule public final ZeebeTestRule testRule = new ZeebeTestRule(); - - private ZeebeClient client; - - @Test - public void test() { - client = testRule.getClient(); - - client - .newDeployCommand() - .addResourceFromClasspath("process.bpmn") - .send() - .join(); - - final ProcessInstanceEvent processInstance = - client - .newCreateInstanceCommand() - .bpmnProcessId("process") - .latestVersion() - .send() - .join(); - } -} -``` - -## Verify the result - -The `ZeebeTestRule` also provides some basic assertions in AssertJ style. The entry point of the assertions is `ZeebeTestRule.assertThat(...)`. - -```java -final ProcessInstanceEvent processInstance = ... - -ZeebeTestRule.assertThat(processInstance) - .isEnded() - .hasPassed("start", "task", "end") - .hasVariable("result", 21.0); -``` diff --git a/docs/apis-clients/java-client/zeebe-process-test.md b/docs/apis-clients/java-client/zeebe-process-test.md new file mode 100644 index 00000000000..9ef8b628056 --- /dev/null +++ b/docs/apis-clients/java-client/zeebe-process-test.md @@ -0,0 +1,246 @@ +--- +id: zeebe-process-test +title: "Zeebe Process Test" +--- + +[Zeebe Process Test](https://github.com/camunda-cloud/zeebe-process-test) allows you to unit test your Camunda Cloud BPMN +processes. It will start a Zeebe test engine and provide you with a set of assertions you can use to verify your process +behaves as expected. + +## Prerequisites + +This library requires the following: + +* Java 17+ when running with an embedded engine (`zeebe-process-test-extension`) +* Java 8+ and Docker when running using testcontainers (`zeebe-process-test-extension-testcontainer`) +* JUnit 5 + +## Dependency + +Zeebe Process Test provides you with two dependencies. Which one you need to use is dependent on the +Java version you are using. + +#### Testcontainers (JDK 8+) + +If you are building your project with a JDK lower than 17, you need to use the `testcontainer` dependency. It +starts a `testcontainer` in which a Zeebe test engine is running. The advantage of using this version +instead of the embedded version is that your code can be implemented independently of the Java +version that is used by the Zeebe engine. The downside is that `testcontainers` provide some +overhead, which means tests will be slower. There is also the extra requirement that Docker must be +running to execute the tests. + +```xml + + io.camunda + zeebe-process-test-extension-testcontainer + X.Y.Z + test + +``` + +#### Embedded (JDK 17+) + +If you are building your project with JDK 17+, you can make use of an embedded Zeebe test engine. The +advantage of using this instead of the `testcontainer` version is that this is the faster solution. +This also does not require Docker to be running. The downside to this solution is that the JDK +requirement is bound to the Java version of the Zeebe engine. Whenever this Java version changes, +you'd either have to [switch to the testcontainer version](#switching-between-testcontainers-and-embedded), +or upgrade your own JDK to match the Zeebe engine. + +```xml + + io.camunda + zeebe-process-test-extension + X.Y.Z + test + +``` + +## Annotation + +Annotate your test class with the `@ZeebeProcessTest` annotation. This annotation will do a couple of things: + +1. It will manage the lifecycle of the testcontainer/embedded test engine. +2. It will create a client which can be used to interact with the engine. +3. It will (optionally) inject three fields in your test class: + 1. `ZeebeTestEngine` - This is the engine that will run your process. It will provide some basic functionality + to help you write your tests, such as waiting for an idle state and increasing the time. + 2. `ZeebeClient` - This is the client that allows you to send commands to the engine, such as + starting a process instance. The interface of this client is identical to the interface you + use to connect to a real Zeebe engine. + 3. `RecordStream` - This gives you access to all the records processed by the engine. + Assertions use the records for verifying expectations. This grants you the freedom to create your own assertions. + +```java +// When using the embedded test engine (Java 17+) +import io.camunda.zeebe.process.test.extension.ZeebeProcessTest; + +// When using testcontainers (Java 8+) +import io.camunda.zeebe.process.test.extension.testcontainer.ZeebeProcessTest; + +@ZeebeProcessTest +class DeploymentAssertTest { + private ZeebeTestEngine engine; + private ZeebeClient client; + private RecordStream recordStream; +} +``` + +## Switching between testcontainers and embedded + +Switching between testcontainers and embedded requires just two steps: + +1. Switch to the relevant dependency. + * Testcontainers: `zeebe-process-test-extension-testcontainer` + * Embedded: `zeebe-process-test-extension` + +2. Change the import of `@ZeebeProcessTest`. + * Testcontainers: `import io.camunda.zeebe.process.test.extension.testcontainer.ZeebeProcessTest;` + * Embedded: `import io.camunda.zeebe.process.test.extension.ZeebeProcessTest;` + +## Assertions + +Start an assertion using the following entry points: + +### Deployment assertions + +```java +DeploymentEvent event = client.newDeployCommand() + .addResourceFromClasspath("my-process.bpmn") + .send() + .join(); +DeploymentAssert assertions = BpmnAssert.assertThat(event); +``` + +### Process instance assertions + +Started by manually sending an event: + +```java +ProcessInstanceEvent event = client.newCreateInstanceCommand() + .bpmnProcessId("") + .latestVersion() + .send() + .join(); +ProcessInstanceAssert assertions = BpmnAssert.assertThat(event); +``` + +```java +ProcessInstanceResult event = client.newCreateInstanceCommand() + .bpmnProcessId("") + .latestVersion() + .withResult() + .send() + .join(); + ProcessInstanceAssert assertions = BpmnAssert.assertThat(event); +``` + +Started by a timer: + +```java +Optional firstProcessInstance = InspectionUtility.findProcessEvents() + .triggeredByTimer(ProcessPackTimerStartEvent.TIMER_ID) + .findFirstProcessInstance(); +ProcessInstanceAssert assertions = BpmnAssert.assertThat(firstProcessInstance.get()); +``` + +Started by a call activity: + +```java +Optional firstProcessInstance = InspectionUtility.findProcessInstances() + .withParentProcessInstanceKey() + .withBpmnProcessId("") + .findFirstProcessInstance(); +ProcessInstanceAssert assertions = BpmnAssert.assertThat(firstProcessInstance.get()); +``` + +### Job assertions + +```java +ActivateJobsResponse response = client.newActivateJobsCommand() + .jobType("") + .maxJobsToActivate(1) + .send() + .join(); +ActivatedJob activatedJob = response.getJobs().get(0); +JobAssert assertions = BpmnAssert.assertThat(activatedJob); +``` + +### Message assertions + +```java +PublishMessageResponse response = client + .newPublishMessageCommand() + .messageName("") + .correlationKey("") + .send() + .join(); +MessageAssert assertions = BpmnAssert.assertThat(response); +``` + +### Incident assertions + +Via a process instance + +```java +ProcessInstanceEvent event = client.newCreateInstanceCommand() + .bpmnProcessId("") + .latestVersion() + .send() + .join(); +IncidentAssert assertions = BpmnAssert.assertThat(event) + .extractingLatestIncident(); +``` + +Via a job: + +```java +ActivateJobsResponse response = client.newActivateJobsCommand() + .jobType("") + .maxJobsToActivate(1) + .send() + .join(); +ActivatedJob activatedJob = response.getJobs().get(0); +IncidentAssert assertions = BpmnAssert.assertThat(activatedJob) + .extractingLatestIncident(); +``` + +## Waiting for idle state + +:::caution +Waiting for idle state is a new feature. When the engine is detected to be idle, it +will wait 30ms before checking again. If it is still idle at that stage, it is considered to be in +an idle state. + +**It is unknown if the 30ms delay is sufficient. Using it could result in flaky tests!** + +Any feedback about the wait for idle state is highly appreciated. Let us know if the delay should be higher or configurable. +Leave your feedback on our [GitHub page](https://github.com/camunda-cloud/zeebe-process-test/issues). +::: + +`engine.waitForIdleState(timeout)` will cause your test to stop executing until the engine has +reached an idle state. If the engine does not reach an idle state within the specified timeout, a +`TimeoutException` will be thrown. + +We have defined an idle state as a state in which the engine makes no progress and is waiting for +new commands or events to trigger. Once the engine has detected it has become idle, it will wait for +a delay (30ms) and check if it is still idle. If this is the case, it is considered to be in idle +state and continue your test. + +## Wait for busy state + +`engine.waitForBusyState(timeout)` will cause your test to stop executing until the engine has +reached a busy state. If the engine does not reach a busy state within the specified timeout, a +`TimeoutException` is thrown. + +We consider the engine to have reached a busy state when any new record/command is processed since +we've started waiting. + +Waiting for a busy state is useful in scenarios where you're expecting the engine to start doing +something, without explicitly triggering it yourself. An example of this would be a process with a +timer event. We can increase the time of the engine, but we cannot trigger the timer explicitly. +Because of this, we should wait for a busy state after increasing the engine time. + +## Examples + +For example tests, refer to [GitHub](https://github.com/camunda-cloud/zeebe-process-test). diff --git a/sidebars.js b/sidebars.js index 26b44e8e45c..705f4a6a215 100644 --- a/sidebars.js +++ b/sidebars.js @@ -341,21 +341,21 @@ module.exports = { "apis-clients/java-client/index", "apis-clients/java-client/job-worker", "apis-clients/java-client/logging", - "apis-clients/java-client/testing", - { - Examples: [ - "apis-clients/java-client-examples/index", - "apis-clients/java-client-examples/process-deploy", - "apis-clients/java-client-examples/process-instance-create", - "apis-clients/java-client-examples/process-instance-create-nonblocking", - "apis-clients/java-client-examples/process-instance-create-with-result", - "apis-clients/java-client-examples/job-worker-open", - "apis-clients/java-client-examples/data-pojo", - "apis-clients/java-client-examples/cluster-topology-request", - ], - }, - ], - }, + "apis-clients/java-client/zeebe-process-test", + { + Examples: [ + "apis-clients/java-client-examples/index", + "apis-clients/java-client-examples/process-deploy", + "apis-clients/java-client-examples/process-instance-create", + "apis-clients/java-client-examples/process-instance-create-nonblocking", + "apis-clients/java-client-examples/process-instance-create-with-result", + "apis-clients/java-client-examples/job-worker-open", + "apis-clients/java-client-examples/data-pojo", + "apis-clients/java-client-examples/cluster-topology-request", + ], + }, + ], + }, { "Go client": [ "apis-clients/go-client/index", diff --git a/static/.htaccess b/static/.htaccess index 158ab4aa558..4fb2350e390 100644 --- a/static/.htaccess +++ b/static/.htaccess @@ -92,6 +92,10 @@ RewriteRule ^docs/components/modeler/desktop-modeler/?$ /docs/components/modeler # workaround for 404 with trailing slashes https://github.com/camunda-cloud/camunda-cloud-documentation/issues/403 RewriteRule ^(.*\.(yaml|bpmn|xml|png|jpeg|jpg|yml|svg))/$ /$1 [R=301,L] +# Replaced deprecated java-client/testing page with java-client/zeebe-process-test page +RewriteRule ^docs/apis-clients/java-client/testing(.*)$ /docs/apis-clients/java-client/zeebe-process-test$1 [R=301,L] +RewriteRule ^docs/apis-clients/java-client/testing$ /docs/apis-clients/java-client/zeebe-process-test [R=301,L] + # rules required after update to docusaurs 2.0.0-beta.15 see https://github.com/camunda-cloud/camunda-cloud-documentation/pull/531 ## index pages are not served as /index/ anymore but /index.html RewriteRule ^(.*)/index/$ /$1 [R=301,L]