pytest-subprocess
pytest-subprocess is a pytest plugin that allows faking the behavior of `subprocess` calls within tests, eliminating the need to rely on actual external processes. It hooks into `subprocess.Popen()` and consequently handles `subprocess.run()`, `subprocess.call()`, `subprocess.check_call()`, and `subprocess.check_output()` methods. The library is actively maintained with regular releases, ensuring compatibility and new features.
Warnings
- gotcha On Python 3.12+, versions prior to 1.5.4 might lead to `ResourceWarning` for unclosed file handles when using `universal_newlines` or `text` arguments with `subprocess.Popen`.
- gotcha Older versions (prior to 1.5.3) contained an incorrect wait timeout calculation, which could lead to unreliable timeout behavior in tests.
- gotcha When running on Windows with Python versions prior to 3.8, passing `Path` objects as arguments to `subprocess` functions might result in a `TypeError`.
- breaking Prior to version 1.4.2, exceptions raised within callback functions passed to `register()` might have been silently ignored by `communicate()`. Version 1.4.2 changed this behavior to raise these exceptions, which could break tests relying on the silent failure.
- gotcha By default, any attempt to run a `subprocess` command that has NOT been registered using `fp.register()` will raise a `ProcessNotRegisteredError`.
- gotcha When using `pytest-cov` to measure coverage of code run in subprocesses, `pytest-cov`'s environment variables must be explicitly passed to the subprocess. If these are not passed, coverage for the subprocess might not be collected.
Install
-
pip install pytest-subprocess
Imports
- fake_process
def test_something(fake_process): # use fake_process here
Quickstart
import subprocess
import pytest
def test_process_command(fp):
# Register a fake command and its expected output
fp.register(['fake-command'], stdout='Hello from fake-command')
# Run the command using subprocess (it will be faked by pytest-subprocess)
process = subprocess.run(['fake-command'], capture_output=True, text=True)
# Assert on the faked process's behavior
assert process.returncode == 0
assert process.stdout.strip() == 'Hello from fake-command'
def test_command_with_args(fp):
fp.register(['greet', 'world'], stdout='Greeting world!')
process = subprocess.run(['greet', 'world'], capture_output=True, text=True)
assert process.returncode == 0
assert process.stdout.strip() == 'Greeting world!'