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.

Easy Python 3.9+ Aug 9, 2026 Modern tooling 15 views 0 copies

Python code

40 lines
Python 3.9+
import 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

stdout
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

  1. Use `monkeypatch` from pytest to temporarily replace `subprocess.run` in tests.
  2. 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

Run this sample

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

Open editor

More from Modern tooling

Related tutorials and quizzes for this topic.