Skip to content

feat: generate process test coverage reports in the BPMN examples - #110

Merged
emaarco merged 7 commits into
mainfrom
feat/process-test-coverage
Nov 5, 2026
Merged

emaarco merged 7 commits into
mainfrom
feat/process-test-coverage

Conversation

@emaarco

@emaarco emaarco commented Nov 5, 2026 •

Copy link
Copy Markdown
Member

Why

The process tests check that each path through the bike-leasing model works, but nothing shows which parts of the model those tests never reach. The CIB seven developer training will cover Process Test Coverage in its testing chapter. The blueprints should produce the report without extra setup, for CIB seven and for every other engine where a working tool exists.

What

Each engine needed a different tool:

Engine Examples Tool Status
CIB seven cibseven-embedded, cibseven-process-engine-api, fullstack-cibseven-embedded, cibseven-remote org.cibseven.community.process_test_coverage 2.2.0 added
Fluxnova fluxnova-embedded org.finos.fluxnova.bpm.test:fluxnova-bpm-testing-process-coverage 2.0.0 added
Camunda 8 zeebe-native, zeebe-process-engine-api, zeebe-vanillabp built into Camunda Process Test was already on, report moved
Operaton operaton-embedded, operaton-process-engine-api, operaton-remote io.github.ronalders:operaton-process-test-coverage 1.0.0 left out, see below
Temporal temporal-native none, it has no BPMN does not apply
  • CIB seven, Spring examples: the spring-test-platform-7 artifact is a test dependency, and a file in the test resources (META-INF/spring/…AutoConfiguration.imports) loads ProcessEngineCoverageConfiguration. Fluxnova's extension registers itself the same way. This keeps the process test classes identical across the cibseven, fluxnova and operaton embedded examples, which the drift check requires.
  • CIB seven, cibseven-remote: this test runs a standalone in-memory engine, so it now uses the junit5-platform-7 extension with a ProcessCoverageInMemProcessEngineConfiguration. The models come from a class-level @Deployment instead of a manual deployment. The engine is now shared by the whole test class instead of rebuilt per test; @Deployment cleans up after each test.
  • Java/Maven variants: kotlin-reflect is now a test dependency in cibseven-embedded, cibseven-remote and fluxnova-embedded. The CIB seven and Fluxnova extensions need it but don't declare it, and without it Spring skips their test execution listener without any error. The tests then fail with Coverage state must be initialized. cibseven-process-engine-api already gets it at compile scope through process-engine-api, so it declares nothing: a test-scoped declaration would narrow it and the packaged app would fail to start. The Kotlin variants already have it on the classpath.
  • Fluxnova, Java/Maven: the coverage extension declares fluxnova-engine and fluxnova-engine-spring 3.0.1 directly, and Maven's nearest-wins rule would put those into the packaged app instead of the starter's 3.1.1. The managed dependency excludes both, so the app keeps 3.1.1 like the Gradle variant.
  • Gradle variants: a system property on the test task moves the report from target/ to build/reports/process-test-coverage/, next to the other reports. Maven keeps the default (target/process-test-coverage/, or target/coverage-report/ for Camunda 8).
  • Docs: every touched example's AGENTS.md says where its report is written. No minimum coverage is enforced; that is a separate decision.

Verification

  • ./gradlew build passed in every touched Gradle variant: the four CIB seven examples, fluxnova-embedded and the three Zeebe examples. Each build wrote a report to build/reports/process-test-coverage/.
  • ./mvnw verify passed in cibseven-embedded, cibseven-process-engine-api, cibseven-remote and fluxnova-embedded (Java/Maven). Each build wrote a report under target/.
  • The Java/Maven variants of the Zeebe examples are unchanged and were not rebuilt.
  • npm run drift, npm run generate:check and npm test pass. drift.lock.yml now locks the new auto-configuration files.
  • tooling/starter/export.sh cibseven-remote java-maven keeps only the Maven report path in the exported AGENTS.md.

Out of scope / Follow-ups

  • Operaton: there is no official extension. The only one is a single-maintainer port (io.github.ronalders, 1.0.0), built against Operaton 1.1.2. With Operaton 2.2.0 and our stack it fails in two ways:

    • it depends on kotlin-logging-jvm 7, but bpmn-to-code-testing brings in 8, and the listener then throws NoSuchMethodError;
    • its CompensationEventCoverageHandler throws an NPE (parameter payload is null) on every compensation path.

    The Operaton examples stay unchanged until a compatible release exists.

  • Upstream: the CIB seven extension (2.2.0) and the Fluxnova extension (2.0.0) should declare kotlin-reflect. Worth an upstream issue.

  • Later: a minimum-coverage assertion as a quality gate, and uploading the reports as CI artifacts.

Refs https://github.com/Miragon/cibseven-developer-training/issues/249

Add the CIB seven community extension Process Test Coverage to the process
tests of cibseven-embedded, cibseven-process-engine-api, cibseven-remote and
fullstack-cibseven-embedded. Spring tests load it through a test
auto-configuration, the remote example's standalone engine through the JUnit 5
extension. Gradle writes the report to build/reports/process-test-coverage.
Add fluxnova-bpm-testing-process-coverage, which registers itself through
Spring auto-configuration. Gradle writes the report to
build/reports/process-test-coverage.
…uild directory

Camunda Process Test already writes a coverage report, but to target/ in the
Gradle variants as well. Point it to build/reports/process-test-coverage and
document the report.
@emaarco
emaarco marked this pull request as ready for review November 5, 2026 18:44
@emaarco
emaarco enabled auto-merge (squash) November 5, 2026 18:44
@emaarco
emaarco merged commit f493320 into main Nov 5, 2026
57 checks passed
@emaarco
emaarco deleted the feat/process-test-coverage branch November 5, 2026 18:51
emaarco added a commit that referenced this pull request Nov 7, 2026
…gateway (#111)

## Why

#110 writes one process test coverage report per test class, but there
was no overview across classes and nothing failed when a path went
untested. Turning on a 100 % minimum surfaced two gaps that held in
every example: the 7-day signature reminder was never tested, and in
cancel-bike-order the condition of the gateway "Cancellation possible?"
sat on the "No" flow, so a cancellable order went through "Clarify
return with supplier" and "Book costs" (ADR-0014 of the Temporal example
already noted that the model reads as inverted).

## What

- **Combined report (CIB seven):** the four CIB seven examples apply the
extension's report aggregator. Gradle loads it from the buildscript
classpath, because no plugin marker is published, and runs
`aggregateProcessTestCoverage` after `test`. Maven runs the `aggregate`
goal in the `test` phase. The result is `all/report.html` in the report
folder.
- **Coverage gate, 100 % per process test class (CIB seven, Fluxnova):**
- The minimum is set in one place, as the extension's system property on
the test task or in Surefire:
`org.cibseven.community.process_test_coverage.ASSERT_AT_LEAST`, or
`process_test_coverage.ASSERT_AT_LEAST` for Fluxnova.
- The Spring examples get a test-only
`ProcessTestCoverageContextCustomizerFactory`, registered in
`META-INF/spring.factories`. It builds the coverage properties with the
extension's builder only for classes ending in `ProcessTest`. Other
classes that run the process, such as `BikeUnavailableTransactionTest`,
are still reported but not gated. The extension's own inclusion patterns
cannot be used for this, because an excluded class that starts a process
fails with "Coverage state must be initialized".
- `cibseven-remote` uses the JUnit 5 extension, whose builder reads the
property itself.
- Running single test methods covers less, so the minimum can be lowered
for that run with `-PprocessTestCoverageMinimum=0` (Gradle) or
`-Dprocess-test-coverage.minimum=0` (Maven).
- There is no gate for Camunda 8, Operaton and Temporal. Camunda Process
Test has no minimum option, Operaton has no working coverage tool (see
#110) and Temporal has no model.
- **Cancellation gateway fix (all 12 examples):**
- The "No" flow now carries `${!cancellationPossible}` (Camunda 8:
`=not(cancellationPossible)`). The models were edited with bpmn-cli, so
each diff is one line, and the generated process APIs were regenerated.
- The Temporal workflow and ADR-0014 follow the same rule: possible
reverses the order right away, not possible waits for the return
clarification and books the costs.
- **Demo dealer:** an order of the custom build `BIKE-CUSTOM` cannot be
cancelled. Every other order still can. The rule is encoded in the order
id prefix `ORDER-CUSTOM-`, so the adapter keeps no state.
- **Process tests:**
- The abort test now covers the "not possible" path and checks that
costs are booked once.
- The abort after an accepted alternative covers the "possible" path,
with no clarify-return task and no costs.
- A new test covers the signature reminder: one reminder, then the
deadline rejects the application.
- **Bruno:**
- `03-abort` and the demo data `07-withdrawn-return-open` order
`BIKE-CUSTOM`, so they keep the clarify-return path.
- The new scenario `11-abort-order-cancelled` shows the direct
cancellation.
- In Temporal, `03-abort` also checks that the application is still
WITHDRAWN after a few seconds.
- **fullstack-cibseven-embedded:**
- "Custom Build" is seeded into the bike catalogue, and the Playwright
abort spec orders it, so the UI still reaches the clarify-return path.
- The withdrawal note (`withdrawRequestedNote`, en/de) now says the
Tasklist step is only needed when the dealer cannot cancel.
- **Docs:** each example's AGENTS.md describes the combined report and
the gate, the review-process agents list `BIKE-CUSTOM` as intended, and
`drift.yml` records why the Fluxnova factory and the Temporal check
differ.

## Verification

- **Clean full builds after the last change, all passing:** `./gradlew
clean build` / `./mvnw -B clean verify` in cibseven-embedded,
cibseven-process-engine-api and cibseven-remote (both variants),
fullstack-cibseven-embedded and fluxnova-embedded (both variants). Each
`BikeLeasingProcessTest` reaches 100 %. The CIB seven report folders
contain `all/report.html`.
- **Gate checked:** without the new reminder test,
`BikeLeasingProcessTest` fails with "matches if the coverage ratio is at
least 1.0" in cibseven-embedded (93 %), cibseven-remote (94.6 %) and
fluxnova-embedded (94.4 %), in both variants.
`BikeUnavailableTransactionTest` (46 %) passes ungated.
- **Single-method runs:** a run of one `BikeLeasingProcessTest` method
fails the gate, and passes with `-PprocessTestCoverageMinimum=0`.
`./mvnw test -Dtest=…#… -Dprocess-test-coverage.minimum=0` passes and
writes `all/`.
- **Other examples:** `./gradlew build` / `./mvnw -B verify` passed in
operaton-embedded, operaton-process-engine-api, operaton-remote,
temporal-native and the three Zeebe examples. These builds ran before
the last whitespace and Bruno-only edits in those examples. The Zeebe
builds ran one at a time, because Camunda Process Test containers time
out under podman load.
- **Repository checks:** `npm run drift`, `npm run generate:check`,
`scripts/check-owner.sh`, `npm test` and `npm --prefix
examples/fullstack-cibseven-embedded/frontend run verify` all pass.
`bpmn-cli validate` reports every edited model valid, with no warnings.
- **Not run:** the Bruno collections and the Playwright e2e suite, which
need a running stack.

## Out of scope / Follow-ups

- The UI copy of `withdrawRequestedNote` (en/de) and the visible "Custom
Build" bike in the fullstack catalogue are user-facing; please confirm
the wording and the choice.
- The extension's Gradle aggregator task is not compatible with the
configuration cache (it holds a `Project`); the cache is off in these
builds.
- The aggregators read every `report.json` under the report folder, so
reports of renamed or deleted test classes stay in `all/` until `clean`.
- The flipped branch in the Temporal workflow is not guarded with
`Workflow.getVersion`; the dev server keeps no state, and on a real
cluster a parked cancel workflow would simply complete without booking
costs.
- An upstream issue for the CIB seven extension: excluding a test class
that starts a process crashes instead of skipping it.

Refs Miragon/cibseven-developer-training#249
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant