Skill: test-worker
Generate a new unit test or update an existing one for a Zeebe job worker class.
This skill follows the project's established test patterns.
IMPORTANT
- No Spring context, no Camunda test runtime in worker unit tests
- Mirror the test to the package-path of the worker file
- Kotlin: use
mockk / io.mockk.*; always call confirmVerified(useCase) after every verify block
- Java: use Mockito (
@ExtendWith(MockitoExtension.class), @Mock, @InjectMocks); call
verifyNoMoreInteractions(useCase) after every verify
- By default, write only one test case; ask for permission before writing more
- Structure each test with
// Given, // When, // Then section comments; omit the comment when that phase is a
single line
Pattern
class YourWorkerTest {
private val useCase = mockk<YourUseCase>()
private val underTest = YourWorker(useCase)
@Test
fun `should perform action when job is received`() {
// Given
val variableId = UUID.fromString("123e4567-e89b-12d3-a456-426614174000")
every { useCase.method(DomainType(variableId)) } just Runs
// When
underTest.handle(variableId)
// Then
verify(exactly = 1) { useCase.method(DomainType(variableId)) }
confirmVerified(useCase)
}
}
Output-variable worker
Use this variant when handle returns Map<String, Any>.
Capture the result and assert the map entries:
@Test
fun `should return output variable when job is received`() {
// Given
val variableId = UUID.fromString("123e4567-e89b-12d3-a456-426614174000")
every { useCase.method(DomainType(variableId)) } returns expectedValue
// When
val result = underTest.handle(variableId)
// Then
verify(exactly = 1) { useCase.method(DomainType(variableId)) }
confirmVerified(useCase)
assertThat(result).containsExactly(entry(Variables.CONSTANT, expectedValue))
}
See references/worker-test-template.kt for the full annotated example.
SDK Context
This project uses:
- Production:
io.camunda:camunda-spring-boot-starter — annotations from io.camunda.client.annotation (
@JobWorker, @Variable, @VariableAsType)
- Testing:
io.camunda:camunda-process-test-spring — test utilities from io.camunda.process.test.api (used in
process integration tests, not in worker unit tests)
Worker unit tests use io.mockk only — no Camunda test runtime is needed.
If you encounter imports or usage patterns not covered by this skill,
ask the user before proceeding how to handle it and whether to add it to the skill.
Instructions
Step 1 – Read and identify
Read the worker file at $ARGUMENTS and extract:
- Language: derive from the file extension —
.kt → Kotlin (mockk), .java → Java (Mockito)
- Worker class name & use-case to be called by the worker
- Variable injection style:
@Variable param: Type — injects one variable by name; the test calls handle(value) with the raw value
@VariableAsType variables: MyVarsClass — injects all variables as a typed object; the test instantiates
the class and passes it, e.g. handle(MyVarsClass(subscriptionId = subscriptionId))
- The
@JobWorker-annotated handle method signature
- The return type of
handle:
Unit (or no explicit return type) → void worker: no result assertion needed
Map<String, Any> → output-variable worker: capture val result = underTest.handle(...) and assert the
returned map in the Then block
- The use-case method called inside
handle and its parameter types
- Domain wrapper types used (e.g.
SubscriptionId)
- Error Handling: If the file at
$ARGUMENTS cannot be read, stop immediately and ask the user for the correct path.
Step 2 – Locate the use-case interface
Every worker must delegate to a use-case:
- Derive the interface name from the constructor parameter identified in Step 1
- Glob for
**/<InterfaceName>.kt (Kotlin) or **/<InterfaceName>.java (Java) under the src/main source tree
- If found, read it to confirm the method signature matches what Step 1 identified
- If not found, search more broadly (
**/*UseCase.kt / **/*UseCase.java) and present the user with a numbered
list of candidates
- If no use-case can be found at all, stop and ask the user to provide the path manually before continuing
Step 3 – Locate the test file
- Mirror the path: replace
src/main/kotlin → src/test/kotlin (Kotlin) or src/main/java → src/test/java (
Java), keeping the same package structure
- Append
Test to the class name (e.g. FooWorker → FooWorkerTest)
- If the file already exists, open it and switch to fix mode; if not, proceed to generate a new file
Step 4 – Determine required test cases
Normally one test per distinct happy-path behavior.
- If the worker calls the use case conditionally or has multiple code paths, list the cases and ask for permission
before writing more than one.
Step 5 – Generate or fix the test file
- Pick the reference template based on the language detected in Step 1
- Kotlin →
references/worker-test-template.kt (mockk)
- Java →
references/worker-test-template.java (Mockito)
- Write the test case(s) based on the template
- In both cases, don't use tests with spring-context and use fixed uuid strings
Step 6 – Write, run, and report
- Write the test file to the located path
- Run all tests in the class via an appropriate Gradle command
- Report the created or updated file path and a brief summary
1---2name: test-worker3description: Generate a unit test for a Zeebe `@JobWorker` class using mockk (Kotlin) or Mockito (Java), without any Spring context or Camunda test runtime. Use when the user asks to "write tests for a job worker", "generate tests for a worker", or "test a @JobWorker".4---5
6# Skill: test-worker
7
8Generate a new unit test or update an existing one for a Zeebe job worker class.
9This skill follows the project's established test patterns.
10
11## IMPORTANT
12
13- No Spring context, no Camunda test runtime in worker unit tests
14- Mirror the test to the package-path of the worker file
15- **Kotlin**: use `mockk` / `io.mockk.*`; always call `confirmVerified(useCase)` after every `verify` block
16- **Java**: use Mockito (`@ExtendWith(MockitoExtension.class)`, `@Mock`, `@InjectMocks`); call
17 `verifyNoMoreInteractions(useCase)` after every `verify`
18- By default, write only **one** test case; ask for permission before writing more
19- Structure each test with `// Given`, `// When`, `// Then` section comments; omit the comment when that phase is a
20 single line
21
22## Pattern
23
24```kotlin
25class YourWorkerTest {
26 private val useCase = mockk<YourUseCase>()
27 private val underTest = YourWorker(useCase)
28
29 @Test
30 fun `should perform action when job is received`() {
31 // Given
32 val variableId = UUID.fromString("123e4567-e89b-12d3-a456-426614174000")
33 every { useCase.method(DomainType(variableId)) } just Runs
34 // When
35 underTest.handle(variableId)
36 // Then
37 verify(exactly = 1) { useCase.method(DomainType(variableId)) }
38 confirmVerified(useCase)
39 }
40}
41```
42
43### Output-variable worker
44
45Use this variant when `handle` returns `Map<String, Any>`.
46Capture the result and assert the map entries:
47
48```kotlin
49 @Test
50fun `should return output variable when job is received`() {
51 // Given
52 val variableId = UUID.fromString("123e4567-e89b-12d3-a456-426614174000")
53 every { useCase.method(DomainType(variableId)) } returns expectedValue
54 // When
55 val result = underTest.handle(variableId)
56 // Then
57 verify(exactly = 1) { useCase.method(DomainType(variableId)) }
58 confirmVerified(useCase)
59 assertThat(result).containsExactly(entry(Variables.CONSTANT, expectedValue))
60}
61```
62
63See `references/worker-test-template.kt` for the full annotated example.
64
65## SDK Context
66
67This project uses:
68
69- **Production**: `io.camunda:camunda-spring-boot-starter` — annotations from `io.camunda.client.annotation` (
70 `@JobWorker`, `@Variable`, `@VariableAsType`)
71- **Testing**: `io.camunda:camunda-process-test-spring` — test utilities from `io.camunda.process.test.api` (used in
72 process integration tests, not in worker unit tests)
73
74Worker unit tests use `io.mockk` only — no Camunda test runtime is needed.
75If you encounter imports or usage patterns not covered by this skill,
76ask the user before proceeding how to handle it and whether to add it to the skill.
77
78## Instructions
79
80### Step 1 – Read and identify
81
82Read the worker file at `$ARGUMENTS` and extract:
83
84- **Language**: derive from the file extension — `.kt` → Kotlin (mockk), `.java` → Java (Mockito)
85- Worker class name & use-case to be called by the worker
86- Variable injection style:
87 - **`@Variable param: Type`** — injects one variable by name; the test calls `handle(value)` with the raw value
88 - **`@VariableAsType variables: MyVarsClass`** — injects all variables as a typed object; the test instantiates
89 the class and passes it, e.g. `handle(MyVarsClass(subscriptionId = subscriptionId))`
90- The `@JobWorker`-annotated `handle` method signature
91- The **return type** of `handle`:
92 - `Unit` (or no explicit return type) → **void worker**: no result assertion needed
93 - `Map<String, Any>` → **output-variable worker**: capture `val result = underTest.handle(...)` and assert the
94 returned map in the Then block
95- The use-case method called inside `handle` and its parameter types
96- Domain wrapper types used (e.g. `SubscriptionId`)
97- Error Handling: If the file at `$ARGUMENTS` cannot be read, stop immediately and ask the user for the correct path.
98
99### Step 2 – Locate the use-case interface
100
101Every worker must delegate to a use-case:
102
103- Derive the interface name from the constructor parameter identified in Step 1
104- Glob for `**/<InterfaceName>.kt` (Kotlin) or `**/<InterfaceName>.java` (Java) under the `src/main` source tree
105- If found, read it to confirm the method signature matches what Step 1 identified
106- If not found, search more broadly (`**/*UseCase.kt` / `**/*UseCase.java`) and present the user with a numbered
107 list of candidates
108- If no use-case can be found at all, **stop** and ask the user to provide the path manually before continuing
109
110### Step 3 – Locate the test file
111
112- Mirror the path: replace `src/main/kotlin` → `src/test/kotlin` (Kotlin) or `src/main/java` → `src/test/java` (
113 Java), keeping the same package structure
114- Append `Test` to the class name (e.g. `FooWorker` → `FooWorkerTest`)
115- If the file already exists, open it and switch to fix mode; if not, proceed to generate a new file
116
117### Step 4 – Determine required test cases
118
119Normally one test per distinct happy-path behavior.
120
121- If the worker calls the use case conditionally or has multiple code paths, list the cases and ask for permission
122 before writing more than one.
123
124### Step 5 – Generate or fix the test file
125
126- Pick the reference template based on the language detected in Step 1
127- **Kotlin** → `references/worker-test-template.kt` (mockk)
128- **Java** → `references/worker-test-template.java` (Mockito)
129- Write the test case(s) based on the template
130- In both cases, don't use tests with spring-context and use fixed uuid strings
131
132### Step 6 – Write, run, and report
133
134- Write the test file to the located path
135- Run all tests in the class via an appropriate Gradle command
136- Report the created or updated file path and a brief summary