A JUnit 5 testing library that provides a Kubernetes mock server for testing Kubernetes client applications
npx @tessl/cli install tessl/maven-io-fabric8--kubernetes-server-mock@7.3.0Kubernetes Server Mock is a comprehensive JUnit 5 testing library that provides a mock Kubernetes API server for testing Kubernetes client applications. It offers two primary modes of operation: Expectations mode for setting up specific request-response patterns and CRUD mode which provides a fully functional in-memory Kubernetes API that can store, retrieve, update, and delete resources just like a real cluster.
<dependency>
<groupId>io.fabric8</groupId>
<artifactId>kubernetes-server-mock</artifactId>
<version>7.3.1</version>
<scope>test</scope>
</dependency>import io.fabric8.kubernetes.client.server.mock.EnableKubernetesMockClient;
import io.fabric8.kubernetes.client.server.mock.KubernetesMockServer;
import io.fabric8.kubernetes.client.server.mock.KubernetesMockServerExtension;
import io.fabric8.kubernetes.client.KubernetesClient;
import io.fabric8.kubernetes.client.NamespacedKubernetesClient;import io.fabric8.kubernetes.client.server.mock.EnableKubernetesMockClient;
import io.fabric8.kubernetes.client.KubernetesClient;
import io.fabric8.kubernetes.client.server.mock.KubernetesMockServer;
import org.junit.jupiter.api.Test;
@EnableKubernetesMockClient
class MyKubernetesTest {
KubernetesMockServer server;
KubernetesClient client;
@Test
void testExpectationsMode() {
// Set up expectations
server.expect().get()
.withPath("/api/v1/namespaces/test/pods/my-pod")
.andReturn(200, new PodBuilder().withNewMetadata().withName("my-pod").endMetadata().build())
.once();
// Use client to perform operations
Pod pod = client.pods().inNamespace("test").withName("my-pod").get();
assertNotNull(pod);
}
}@EnableKubernetesMockClient(crud = true)
class MyCrudTest {
KubernetesMockServer server;
KubernetesClient client;
@Test
void testCrudMode() {
// In CRUD mode, the server acts like a real Kubernetes API
Pod pod = new PodBuilder()
.withNewMetadata().withName("test-pod").withNamespace("default").endMetadata()
.build();
// Create a pod
Pod created = client.pods().inNamespace("default").resource(pod).create();
// Retrieve the pod
Pod retrieved = client.pods().inNamespace("default").withName("test-pod").get();
assertEquals("test-pod", retrieved.getMetadata().getName());
}
}import io.fabric8.kubernetes.client.server.mock.KubernetesMockServer;
class ManualSetupTest {
@Test
void testManualSetup() {
KubernetesMockServer server = new KubernetesMockServer();
server.init();
try (KubernetesClient client = server.createClient()) {
// Test operations
server.expect().get()
.withPath("/api/v1/namespaces")
.andReturn(200, new NamespaceListBuilder().build())
.once();
NamespaceList namespaces = client.namespaces().list();
assertNotNull(namespaces);
} finally {
server.destroy();
}
}
}Kubernetes Server Mock is built around several key components:
KubernetesMockServer class that provides the main server functionality and client creationKubernetesMockServerExtension for automatic test lifecycle managementCore mock server lifecycle management including initialization, configuration, and client creation. Essential for all testing scenarios.
public class KubernetesMockServer extends DefaultMockServer
implements Resetable, CustomResourceAware {
public KubernetesMockServer();
public KubernetesMockServer(boolean useHttps);
public void init();
public void init(InetAddress address, int port);
public void destroy();
public NamespacedKubernetesClient createClient();
public NamespacedKubernetesClient createClient(HttpClient.Factory factory);
public NamespacedKubernetesClient createClient(
Consumer<KubernetesClientBuilder> kubernetesClientBuilderCustomizer);
}Automatic mock server setup and teardown for JUnit 5 tests with annotation-based configuration. Handles both static and instance field injection.
@Target({ TYPE, METHOD, ANNOTATION_TYPE })
@Retention(RUNTIME)
@ExtendWith(KubernetesMockServerExtension.class)
public @interface EnableKubernetesMockClient {
boolean https() default true;
boolean crud() default false;
Class<? extends Consumer<KubernetesClientBuilder>> kubernetesClientBuilderCustomizer()
default KubernetesClientBuilderCustomizer.class;
}
public class KubernetesMockServerExtension
implements AfterEachCallback, AfterAllCallback, BeforeEachCallback, BeforeAllCallback {
}Full in-memory Kubernetes API implementation supporting all standard CRUD operations, resource watching, and advanced features like patches and finalizers.
public class KubernetesCrudDispatcher extends CrudDispatcher
implements KubernetesCrudPersistence, CustomResourceAware {
public KubernetesCrudDispatcher();
public KubernetesCrudDispatcher(List<CustomResourceDefinitionContext> crdContexts);
public MockResponse handleCreate(RecordedRequest request);
public MockResponse handleUpdate(RecordedRequest request);
public MockResponse handleGet(String path);
public MockResponse handlePatch(RecordedRequest request);
public MockResponse handleDelete(String path);
public MockResponse handleWatch(String path);
}Low-level HTTP request handlers that implement individual CRUD operations. These handlers process POST, PUT, and PATCH requests with full Kubernetes compatibility.
public interface KubernetesCrudDispatcherHandler {
MockResponse handle(String path, String contentType, String requestBody)
throws KubernetesCrudDispatcherException;
default void validatePath(AttributeSet query, JsonNode updatedResource);
default void validateResourceVersion(JsonNode currentResource, JsonNode updatedResource);
default GenericKubernetesResource validateRequestBody(String requestBody);
}
public class PostHandler implements KubernetesCrudDispatcherHandler;
public class PutHandler implements KubernetesCrudDispatcherHandler;
public class PatchHandler implements KubernetesCrudDispatcherHandler;Support for Custom Resource Definitions and Custom Resources with automatic API discovery and full CRUD operations.
public interface CustomResourceAware {
void expectCustomResource(CustomResourceDefinitionContext rdc);
}
public class CustomResourceDefinitionProcessor {
public void addCrdContext(CustomResourceDefinitionContext context);
public String getApiResources(String path);
public boolean isStatusSubresourceEnabledForResource(AttributeSet pathAttributes);
}WebSocket-based functionality for watch operations and exec/attach commands with proper stream handling and message formatting.
public class WatchEventsListener {
public void sendWebSocketResponse(String resource, Watcher.Action action);
public boolean attributeMatches(AttributeSet attributes);
}
public class OutputStreamMessage extends WebSocketMessage {
public OutputStreamMessage(String body);
}
public class ErrorStreamMessage extends WebSocketMessage {
public ErrorStreamMessage(String body);
}
public class StatusStreamMessage extends WebSocketMessage {
public StatusStreamMessage(String body);
}Core functionality for parsing Kubernetes API paths and extracting resource metadata into structured attribute sets. Essential for resource matching, filtering, and categorization.
public class KubernetesAttributesExtractor implements AttributeExtractor {
public static final String KIND = "kind";
public static final String API = "api";
public static final String VERSION = "version";
public static final String NAME = "name";
public static final String NAMESPACE = "namespace";
public static final String PLURAL = "plural";
public Map<String, String> fromKubernetesPath(String path);
public AttributeSet fromPath(String path);
public AttributeSet fromResource(String resourceString);
public AttributeSet extract(HasMetadata hasMetadata);
}public interface Resetable {
void reset();
}
public interface CustomResourceAware {
void expectCustomResource(CustomResourceDefinitionContext rdc);
}
public interface KubernetesCrudPersistence {
long requestResourceVersion();
AttributeSet getKey(String path);
Map.Entry<AttributeSet, String> findResource(AttributeSet attributes);
boolean isStatusSubresourceEnabledForResource(String path);
void processEvent(String path, AttributeSet pathAttributes, AttributeSet oldAttributes,
GenericKubernetesResource resource, String newState);
}// From mockwebserver library - represents resource attributes for matching
public class AttributeSet {
// Used internally for resource identification and filtering
// Contains key-value pairs extracted from Kubernetes resources
}
// From kubernetes-client library - generic Kubernetes resource representation
public class GenericKubernetesResource {
// Represents any Kubernetes resource as a generic object
// Used for custom resources and dynamic resource handling
}
// From kubernetes-client library - custom resource definition context
public class CustomResourceDefinitionContext {
public String getGroup();
public String getVersion();
public String getKind();
public String getPlural();
public boolean isNamespaceScoped();
}public class KubernetesCrudDispatcherException extends Exception {
/**
* Create exception with message and HTTP status code
* @param message - Error message
* @param code - HTTP status code (400, 409, 422, etc.)
*/
public KubernetesCrudDispatcherException(String message, Integer code);
/**
* Create exception with detailed validation information
* @param message - Error message
* @param code - HTTP status code
* @param kind - Resource kind that failed validation
* @param requiredFields - Missing required field names
*/
public KubernetesCrudDispatcherException(String message, Integer code, String kind, String... requiredFields);
/**
* Get HTTP status code for the error
* @return HTTP status code (defaults to 400 if not specified)
*/
public int getCode();
/**
* Convert exception to Kubernetes Status object
* @return Status object with error details and field validation information
*/
public Status toStatus();
/**
* Convert exception to JSON Status response body
* @return JSON string containing Kubernetes Status object
*/
public String toStatusBody();
}