Skip to content

prefer-injected-dependency-over-monkeypatch

Tests should inject dependencies instead of replacing attributes through ambient patching.

Why

Attribute patching hides collaborators and configuration behind ambient module or object state, coupling tests to lookup locations instead of an explicit boundary. Pytest monkeypatch remains appropriate for reversible process state such as environment variables and the working directory.

Fix

Pass the collaborator, settings, clock, sleeper, random source, client, or factory explicitly; prefer a real in-process dependency, framework override, or purpose-built ABC/Protocol fake. If a runtime boundary is inherently global, or intercepting its lookup is the behavior under test, suppress SARJ445 at that call with the concrete reason an owned injection seam would invalidate the test.

Examples

Before — flagged Test replaces an application collaborator
tests/test_checkout.py
def test_checkout(monkeypatch):
monkeypatch.setattr(checkout, "charge_card", lambda _card: True)
assert checkout.run() == "paid"
After — preferred Test injects a purpose-built collaborator
tests/test_checkout.py
def test_checkout(recording_gateway):
service = CheckoutService(payment_gateway=recording_gateway)
assert service.run() == "paid"