How to Mock a Kubeflow Pipeline in Python

Build a minimal in-memory mock of a Kubeflow pipeline DAG using dataclasses and OrderedDict to chain component functions.

Medium Python 3.9+ Aug 9, 2026 ML engineering pipelines 14 views 0 copies

Python code

48 lines
Python 3.9+
from typing import Dict, Any
from dataclasses import dataclass, field
from collections import OrderedDict


@dataclass
class KubeflowPipelineMock:
    """A minimal mock of a Kubeflow pipeline DAG."""
    name: str
    components: OrderedDict[str, callable] = field(default_factory=OrderedDict)

    def add_component(self, name: str, func: callable) -> "KubeflowPipelineMock":
        """Register a component (function) in the pipeline."""
        self.components[name] = func
        return self

    def execute(self, inputs: Dict[str, Any]) -> Dict[str, Any]:
        """Run the pipeline, passing outputs as inputs to the next stage."""
        data = dict(inputs)
        for comp_name, comp_func in self.components.items():
            result = comp_func(data)
            data.update(result)
            print(f"[{comp_name}] -> {list(result.keys())}")
        return data


def load_step(context: Dict[str, Any]) -> Dict[str, Any]:
    return {"loaded": context.get("raw", "default")}


def transform_step(context: Dict[str, Any]) -> Dict[str, Any]:
    return {"transformed": context["loaded"].upper()}


def save_step(context: Dict[str, Any]) -> Dict[str, Any]:
    return {"saved": f"final: {context['transformed']}"}


if __name__ == "__main__":
    pipeline = (
        KubeflowPipelineMock(name="sample-dag")
        .add_component("load", load_step)
        .add_component("transform", transform_step)
        .add_component("save", save_step)
    )
    outputs = pipeline.execute({"raw": "hello world"})
    print(f"Pipeline '{pipeline.name}' finished.")
    print(f"Final outputs: {outputs}")

Output

stdout
[load] -> ['loaded']
[transform] -> ['transformed']
[save] -> ['saved']
Pipeline 'sample-dag' finished.
Final outputs: {'raw': 'hello world', 'loaded': 'hello world', 'transformed': 'HELLO WORLD', 'saved': 'final: HELLO WORLD'}

How it works

The KubeflowPipelineMock class uses a dataclass to store the pipeline name and an OrderedDict that preserves component registration order. Each component is a plain function that takes a dictionary context and returns a dictionary of new outputs. The execute method iterates through components in order, passing the accumulated data as input and merging returned keys back into the shared context. load_step uses .get() for safe default handling, while transform_step and save_step assume earlier keys exist, mirroring real pipeline dependencies.

Common mistakes

  • Forgetting that components receive the full accumulating context, not just their own inputs
  • Using a regular dict instead of OrderedDict, which can reorder components in older Python versions
  • Not handling missing keys when a component depends on a previous output

Variations

  1. Use a list of (name, func) tuples instead of OrderedDict for a simpler definition
  2. Wrap real Kubeflow component builders (e.g., from `kfp.dsl`) inside these mock functions for offline testing

Real-world use cases

  • Testing ML pipeline logic locally without a running Kubeflow cluster or Kubernetes dependencies
  • Rapid prototyping of new data processing steps before containerizing them as Kubeflow components
  • Writing unit tests that verify data flows through feature engineering, training, and evaluation stages in sequence

Sponsored

Run this sample

Open the browser IDE to tweak the example and see results without installing anything.

Open editor

More from ML engineering pipelines

Related tutorials and quizzes for this topic.