Skip to content

Commit 027f0e2

Browse files
committed
1.0.0 beta.6/test support (#108)
1 parent 07a4e7c commit 027f0e2

File tree

4 files changed

+523
-198
lines changed

4 files changed

+523
-198
lines changed

docs/testing/flamingock-bdd-api.md

Lines changed: 174 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,174 @@
1+
---
2+
title: Flamingock BDD API
3+
sidebar_position: 2.5
4+
---
5+
6+
## Introduction
7+
8+
Flamingock's test support framework provides a fluent BDD-style API for writing integration tests. This API is shared between standalone and Spring Boot tests — once you understand these concepts, you can apply them in either context.
9+
10+
The API follows a **Given-When-Then** pattern:
11+
12+
```java
13+
FlamingockTestSupport
14+
.givenBuilder(builder) // Given: configure the builder
15+
.andExistingAudit(APPLIED(PreviousChange.class)) // Given: set up existing audit state
16+
.whenRun() // When: trigger execution
17+
.thenExpectAuditFinalStateSequence(...) // Then: define expectations
18+
.verify(); // Execute and validate
19+
```
20+
21+
:::info Lazy execution
22+
All methods are **intermediate operations** — nothing executes until `verify()` is called. This allows you to build complex test scenarios before running them.
23+
:::
24+
25+
26+
## AuditEntryDefinition
27+
28+
Factory methods to define expected audit entries. Import statically for cleaner tests:
29+
30+
```java
31+
import static io.flamingock.support.domain.AuditEntryDefinition.*;
32+
```
33+
34+
### String-based (manual)
35+
36+
Specify the change ID directly:
37+
38+
```java
39+
APPLIED("change-id")
40+
FAILED("change-id")
41+
ROLLED_BACK("change-id")
42+
ROLLBACK_FAILED("change-id")
43+
```
44+
45+
### Class-based (recommended)
46+
47+
Pass the change class to auto-extract metadata from annotations:
48+
49+
```java
50+
APPLIED(MyChange.class)
51+
FAILED(MyChange.class)
52+
ROLLED_BACK(MyChange.class)
53+
ROLLBACK_FAILED(MyChange.class)
54+
```
55+
56+
This approach automatically extracts:
57+
- `changeId` from `@Change` annotation
58+
- `author` from `@Change` annotation
59+
- `className` and `methodName`
60+
- `targetSystemId` from `@TargetSystem` annotation
61+
- `order` and `transactional`
62+
63+
### Fluent builder
64+
65+
Add or override specific fields:
66+
67+
```java
68+
APPLIED(MyChange.class)
69+
.withAuthor("custom-author")
70+
.withTargetSystemId("mongodb-main")
71+
.withErrorTrace("Expected error message")
72+
.withTransactional(true)
73+
.withOrder("001")
74+
```
75+
76+
### Selective field validation
77+
78+
The validator only checks fields that are **set** in the definition:
79+
80+
| Definition | Fields validated |
81+
|------------|------------------|
82+
| `APPLIED("my-change")` | `changeId`, `state` |
83+
| `APPLIED(MyChange.class)` | `changeId`, `state`, `author`, `className`, `methodName`, `order`, `transactional`, `targetSystemId`, `recoveryStrategy` |
84+
| `APPLIED("id").withAuthor("x")` | `changeId`, `state`, `author` |
85+
86+
87+
## WhenStage
88+
89+
The `whenRun()` method marks the transition from setup to expectations:
90+
91+
```java
92+
.givenBuilder(builder)
93+
.andExistingAudit(APPLIED(PreviousChange.class))
94+
.whenRun() // Transition point
95+
.thenExpect...
96+
```
97+
98+
99+
## Validators
100+
101+
### ExpectAuditFinalStateSequence
102+
103+
Validates the final audit state after execution:
104+
105+
```java
106+
.whenRun()
107+
.thenExpectAuditFinalStateSequence(
108+
APPLIED(Change1.class),
109+
APPLIED(Change2.class)
110+
)
111+
.verify();
112+
```
113+
114+
**Behavior:**
115+
- Validates only **final states**: `APPLIED`, `FAILED`, `ROLLED_BACK`, `ROLLBACK_FAILED`
116+
- Filters out intermediate states like `STARTED`
117+
- Requires **exact count match** — if 3 changes executed, provide exactly 3 definitions
118+
- Preserves **order** — expected[0] must match actual[0], etc.
119+
120+
### ExpectException
121+
122+
Expects an exception to be thrown during execution:
123+
124+
```java
125+
.whenRun()
126+
.thenExpectException(PipelineExecutionException.class, ex -> {
127+
assertTrue(ex.getMessage().contains("Expected error"));
128+
})
129+
.verify();
130+
```
131+
132+
If you don't need to validate the exception details, use the single-parameter overload:
133+
134+
```java
135+
.thenExpectException(PipelineExecutionException.class)
136+
```
137+
138+
## Final states
139+
140+
The audit log may contain multiple entries per change (e.g., `STARTED` then `APPLIED`). Validators filter out intermediate states and only consider final outcomes:
141+
142+
| State | Meaning |
143+
|-------|---------|
144+
| `APPLIED` | Change successfully applied |
145+
| `FAILED` | Change execution failed |
146+
| `ROLLED_BACK` | Change was rolled back after failure |
147+
| `ROLLBACK_FAILED` | Rollback itself failed |
148+
149+
150+
## Complete example
151+
152+
```java
153+
import static io.flamingock.support.domain.AuditEntryDefinition.*;
154+
155+
@Test
156+
void shouldHandlePartialFailureWithRollback() {
157+
FlamingockTestSupport
158+
.givenBuilder(builder)
159+
.andExistingAudit(
160+
APPLIED(InitialSetupChange.class) // Already applied in previous run
161+
)
162+
.whenRun()
163+
.thenExpectException(PipelineExecutionException.class, ex -> {
164+
assertThat(ex.getMessage()).contains("Intentional failure");
165+
})
166+
.andExpectAuditFinalStateSequence(
167+
APPLIED(InitialSetupChange.class), // Unchanged from precondition
168+
APPLIED(SuccessfulChange.class), // New change succeeded
169+
FAILED(FailingChange.class), // This change failed
170+
ROLLED_BACK(FailingChange.class) // And was rolled back
171+
)
172+
.verify();
173+
}
174+
```

0 commit comments

Comments
 (0)