How to Mock a CI Pipeline with Build, Test, and Deploy Stages in Python
Simulate a three-stage CI pipeline (build, test, deploy) in Python with random pass/fail logic, early exit on failure, and measured stage durations.
Python code
43 linesimport time
import random
from dataclasses import dataclass
@dataclass
class StageResult:
name: str
status: str
duration: float
def run_stage(name: str, success_chance: float = 0.9) -> StageResult:
"""Simulate a pipeline stage with random success/failure."""
start = time.time()
time.sleep(random.uniform(0.05, 0.2)) # Simulate work
status = "passed" if random.random() < success_chance else "failed"
return StageResult(name, status, round(time.time() - start, 2))
def run_pipeline(build_chance: float = 0.95, test_chance: float = 0.9,
deploy_chance: float = 0.85) -> list[StageResult]:
"""Execute the CI pipeline: build -> test -> deploy (mock)."""
results = []
build = run_stage("build", build_chance)
results.append(build)
if build.status == "failed":
return results # Stop pipeline on build failure
tests = run_stage("test", test_chance)
results.append(tests)
if tests.status == "failed":
return results # Stop pipeline on test failure
deploy = run_stage("deploy", deploy_chance)
results.append(deploy)
return results
if __name__ == "__main__":
random.seed(42) # Deterministic output for demo
for result in run_pipeline():
print(f"{result.name}: {result.status} ({result.duration}s)")
Output
build: passed (0.07s)
test: failed (0.12s)
How it works
The @dataclass decorator automatically creates an __init__ and string representation for StageResult, keeping the model clean. run_stage uses time.sleep to mimic work and random.random with a success-chance threshold to determine status. run_pipeline chains stages and short-circuits by returning early when a stage fails, mirroring real CI behavior. Time is recorded with time.time() and rounded to two decimals for readable output. The random.seed(42) call makes the demo reproducible.
Common mistakes
- Forgetting that early return stops all subsequent stages — deploy never runs if tests fail.
- Using `time.sleep` in production code — it's only for simulation here.
- Not rounding durations, producing noisy floats instead of clean output.
- Assuming deterministic results without calling `random.seed`.
Variations
- Replace `random.random` with a mocked external API call to simulate real CI runners.
- Use `asyncio` with `await asyncio.sleep` for concurrent mock stages.
Real-world use cases
- Predicting pipeline behavior and failure rates before provisioning real CI infrastructure.
- Unit-testing deployment automation logic that must handle stage failures gracefully.
- Demoing CI/CD concepts in trainings or talks without needing a live build system.
Sponsored
More from Production deployment patterns
- Auto Rollback on Error Rate Exceeded in Python medium
- Automate Semantic Versioning with Conventional Commits in Python medium
- Design a Data Helper for Beginners in Python easy
- Docker healthcheck CMD mock in Python easy
- Generate a Mock Artifact Version Tag in Python easy
- Generate a docker-compose.yml with mock services in Python easy
Keep learning
Related tutorials and quizzes for this topic.