diff --git a/docs/content/en/docs/documentation/testing.md b/docs/content/en/docs/documentation/testing.md
index d65a945320..271a2d1dc7 100644
--- a/docs/content/en/docs/documentation/testing.md
+++ b/docs/content/en/docs/documentation/testing.md
@@ -55,7 +55,7 @@ when(context.getSecondaryResource(Deployment.class)).thenReturn(Optional.of(depl
## Integration Testing with `LocallyRunOperatorExtension`
-For integration tests, JOSDK provides a JUnit 5 extension that starts your operator locally and
+For integration tests, JOSDK provides a JUnit extension that starts your operator locally and
connects it to a real Kubernetes cluster (e.g. a local Kind or Minikube cluster). It automatically:
- Creates an isolated test namespace
@@ -153,13 +153,60 @@ LocallyRunOperatorExtension extension =
void shouldReconcileExactlyOnce() {
extension.create(testResource());
- await().untilAsserted(() -> {
+ await().pollDelay(Duration.ofSeconds(1)).untilAsserted(() -> {
var reconciler = extension.getReconcilerOfType(MyReconciler.class);
assertThat(reconciler.getReconcileCount()).isEqualTo(1);
});
}
```
+## Using Fabric8 `@KubeAPITest` for Realistic API Testing
+
+For tests that need a more realistic Kubernetes API (including watches, status subresources, and
+server-side apply), the Fabric8 client provides the
+[`@KubeAPITest`](https://github.com/fabric8io/kubernetes-client/blob/main/doc/kube-api-test.md)
+annotation. It starts a lightweight Kubernetes API server that behaves more closely to a real cluster than
+the mock server (see below). The API Server starts quickly, so it is suitable to run it from unit tests, even separately
+for each test case if needed. In addition to that comes handy if your CI does not support running tools like
+Kind and/or Minikube.
+
+```xml
+
+ io.fabric8
+ kubernetes-junit-jupiter
+ ${fabric8-client.version}
+ test
+
+```
+
+```java
+@KubeAPITest // runs a Kubernetes API Server binary
+class MyReconcilerKubeAPITest {
+
+ static KubernetesClient client; // injects a client
+
+ @RegisterExtension
+ LocallyRunOperatorExtension extension =
+ LocallyRunOperatorExtension.builder()
+ .withConfigurationService(o -> o.withCloseClientOnStop(false))
+ // KubeAPITest does not support deleting namespaces
+ .waitForNamespaceDeletion(false)
+ .withKubernetesClient(client) // using the injected client
+ .withReconciler(new MyReconciler())
+ .build();
+
+ @Test
+ void shouldReconcileExactlyOnce() {
+ extension.create(testResource());
+
+ await().pollDelay(Duration.ofSeconds(1)).untilAsserted(() -> {
+ var reconciler = extension.getReconcilerOfType(MyReconciler.class);
+ assertThat(reconciler.getReconcileCount()).isEqualTo(1);
+ });
+ }
+}
+```
+
## Testing with a Cluster-Deployed Operator
For end-to-end tests where the operator runs as a container in the cluster (e.g. to test the
@@ -270,55 +317,9 @@ class MyReconcilerMockTest {
```
The `crud = true` flag enables automatic CRUD behavior: resources you create are stored and can be
-retrieved, updated, and deleted, simulating a real API server. Without it, you would need to set up
+retrieved, updated, and deleted, simulating a real API Server. Without it, you would need to set up
explicit request/response expectations.
-## Using Fabric8 `@KubeAPITest` for Realistic API Testing
-
-For tests that need a more realistic Kubernetes API (including watches, status subresources, and
-server-side apply), the Fabric8 client provides the
-[`@KubeAPITest`](https://github.com/fabric8io/kubernetes-client/blob/main/doc/kube-api-test.md)
-annotation. It starts a lightweight Kubernetes API server that behaves more closely to a real cluster than
-the mock server. The API Server starts quickly, so it is suitable to run it from unit tests, even separately
-for each test case if needed. In addition to that comes handy if your CI does not support running tools like
-Kind and/or Minikube.
-
-```xml
-
- io.fabric8
- kubernetes-junit-jupiter
- ${fabric8-client.version}
- test
-
-```
-
-```java
-@KubeAPITest
-class MyReconcilerKubeAPITest {
-
- KubernetesClient client;
-
- @Test
- void shouldHandleStatusUpdates() {
- // The API server supports watches, SSA, and status subresources
- client.resource(testCRD()).create();
- client.resource(testCustomResource()).create();
-
- var reconciler = new MyReconciler();
- var context = mock(Context.class);
- when(context.getClient()).thenReturn(client);
-
- var resource = client.resources(MyCustomResource.class)
- .withName("test").get();
- reconciler.reconcile(resource, context);
-
- var updated = client.resources(MyCustomResource.class)
- .withName("test").get();
- assertThat(updated.getStatus().getState()).isEqualTo("Ready");
- }
-}
-```
-
## Multi-Reconciliation Testing Pattern
Operator reconciliation is often a multi-step process. A realistic test exercises your reconciler