How to Mock subprocess.run returncode in Python
Simulate subprocess.run return codes in tests with unittest.mock.patch and CompletedProcess.
Python code
18 linesimport subprocess
from unittest.mock import patch
def run_command(cmd):
result = subprocess.run(cmd, capture_output=True, text=True)
return result.returncode
if __name__ == "__main__":
with patch("subprocess.run") as mock_run:
# Simulate a successful command (returncode 0)
mock_run.return_value = subprocess.CompletedProcess(args=["ls"], returncode=0)
print(f"Success case returncode: {run_command(['ls'])}")
# Simulate a failed command (returncode 1)
mock_run.return_value = subprocess.CompletedProcess(args=["false"], returncode=1)
print(f"Failure case returncode: {run_command(['false'])}")
Output
Success case returncode: 0
Failure case returncode: 1
How it works
patch('subprocess.run') replaces the real subprocess.run with a MagicMock, so calls are captured and you control the return value. subprocess.CompletedProcess is a built-in dataclass with attributes like args and returncode, making it perfect for realistic mocks. Setting mock_run.return_value to a CompletedProcess with the desired returncode simulates success or failure without executing any actual command. This keeps tests fast, deterministic, and avoids side effects on the real system.
Common mistakes
- Mocking subprocess.run but forgetting to set return_value, which defaults to a MagicMock and can cause AttributeErrors.
- Using subprocess.CompletedProcess without required arguments like `args`.
- Patching `subprocess.Popen` when the code uses `subprocess.run` internally — patch what the code actually calls.
Variations
- Use `unittest.mock.patch.object(subprocess, 'run')` to patch more explicitly.
- Use a side_effect with a list of CompletedProcess objects to simulate different returncodes in sequence.
Real-world use cases
- Unit-testing scripts that branch on the exit code of system commands like `git` or `pip`.
- Verifying that automation retries failed commands by simulating non-zero returncodes without shelling out.
- Testing CLI wrapper functions that should log or raise when a subprocess fails.
Sponsored
More from Testing & modern typing
- Capture stdout and stderr with pytest capsys easy
- Characterization Test for Legacy Python Code medium
- Dataclass with Type Hints Fields in Python easy
- Dependency Injection in Python for Testability easy
- Design Data Helpers with Python TypedDict and Literal easy
- Fix and Test a Regression Bug in Python with Unit Tests easy
Keep learning
Related tutorials and quizzes for this topic.