How to Mock Commitizen Version Bump in Python
Simulate commitizen's version bump logic and mock the subprocess call to avoid real execution in tests.
Python code
40 linesimport subprocess
from unittest.mock import patch, MagicMock
def bump_version(current_version: str, increment: str = "patch") -> str:
"""Simulate commitizen's version bump logic."""
major, minor, patch = map(int, current_version.split("."))
if increment == "major":
major += 1
minor = 0
patch = 0
elif increment == "minor":
minor += 1
patch = 0
elif increment == "patch":
patch += 1
else:
raise ValueError(f"Unknown increment: {increment}")
return f"{major}.{minor}.{patch}"
def run_commitizen_bump() -> str:
"""Mock the subprocess call to commitizen to avoid real execution."""
with patch("subprocess.run") as mock_run:
mock_run.return_value = MagicMock(
returncode=0,
stdout=b"bump: version 1.2.3 -> 1.2.4\n",
stderr=b""
)
result = subprocess.run(
["cz", "bump", "--yes"],
capture_output=True,
check=True
)
return result.stdout.decode().strip()
if __name__ == "__main__":
print(f"Direct logic: {bump_version('1.2.3')}")
print(f"Mocked external call: {run_commitizen_bump()}")
Output
Direct logic: 1.2.4
Mocked external call: bump: version 1.2.3 -> 1.2.4
How it works
The bump_version function replicates commitizen's increment logic by parsing the version string and updating major, minor, or patch components. The run_commitizen_bump function uses unittest.mock.patch to replace subprocess.run, returning a MagicMock with a predefined stdout, so the code never actually runs cz — perfect for CI or development environments where you want to test the wrapper without side effects. The mocked run returns a realistic output that matches what commitizen would print, ensuring the function's return value is correct. This pattern isolates external tooling, making unit tests fast and deterministic.
Common mistakes
- Forgetting to decode stdout, which remains bytes unless converted to a string.
- Patching the wrong target — always patch `subprocess.run`, not `run_commitizen_bump` if you call the imported function directly.
- Not setting `check=True` handling; MagicMock won't raise CalledProcessError unless configured.
Variations
- Use `monkeypatch` from pytest to temporarily replace `subprocess.run` in tests.
- Wrap the mock in a context manager with `side_effect` to simulate different return codes or exceptions.
Real-world use cases
- Testing version bump automation without triggering real Git tags or releases in your CI pipeline.
- Simulating commitizen's output in unit tests for release scripts to validate version string parsing.
- Developing deployment tooling that invokes `cz bump` and you need to verify the wrapper's behavior offline.
Sponsored
More from Modern tooling
- Build a Recipe Runner Mock in Python easy
- Build a Textual TUI App Skeleton in Python easy
- Configure ruff linter rules in pyproject.toml with Python easy
- Data Conversion Helper Functions in Python easy
- How to Bind and Mock structlog Context in Python medium
- How to Build a Chainable Filter Helper in Python easy
Keep learning
Related tutorials and quizzes for this topic.