ProvenPin Install

pytest-mock: patch, autospec, spies and call assertions: working versions (tested 2026-10-18)

Core pytest-mock patterns on a small notification service: loose mocker.patch, autospec signature checks, mocker.spy over real methods, patch.object stubs and assert_called_once_with.

passed5 passed · 0 failed2026-10-18 03:57 UTC
Status
passed
Last passed
2026-10-18 03:57 UTC
Tests
5 / 5
Language
Python
Runtime
Python 3.13.16
pinned versions2
pytest-mock@3.17.0pytest@9.1.1
run it
uv venv -q .venv && uv pip install -q --python .venv/bin/python -r requirements.txt
.venv/bin/python -m pytest -q
test environment
Python 3.13.16
linux 6.17.0-1022-azure
x64
runner: github-actions 39095370682

The code

Exactly the files that passed. Your agent can pull them with get_recipe("pytest-mock-spy-autospec") (install), or see them on GitHub.

requirements.txt
pytest-mock==3.17.0
pytest==9.1.1
service.py
import smtplib
from dataclasses import dataclass, field

@dataclass
class Notifier:
    sent: list = field(default_factory=list)

    def send(self, to: str, body: str) -> int:
        if not to:
            raise ValueError("missing recipient")
        self.sent.append((to, body))
        return len(self.sent)

def broadcast(notifier: Notifier, recipients: list, body: str) -> int:
    total = 0
    for to in recipients:
        total += notifier.send(to, body)
    return total

def send_via_smtp(host: str, port: int, to: str, body: str) -> bool:
    with smtplib.SMTP(host, port, timeout=5) as smtp:
        smtp.sendmail("[email protected]", [to], body)
    return True
tests/test_service.py
import pytest
from service import Notifier, broadcast, send_via_smtp

def test_plain_patch_accepts_anything(mocker):
    n = Notifier()
    mock_send = mocker.patch.object(n, "send")
    n.send("[email protected]", "hi", "extra-nonsense", typo=True)
    mock_send.assert_called_once_with("[email protected]", "hi", "extra-nonsense", typo=True)

def test_autospec_enforces_signature(mocker):
    n = Notifier()
    mock_send = mocker.patch.object(n, "send", autospec=True)
    with pytest.raises(TypeError):
        n.send("[email protected]")
    mock_send.assert_not_called()

def test_spy_runs_real_method(mocker):
    n = Notifier()
    spy = mocker.spy(n, "send")
    assert broadcast(n, ["[email protected]", "[email protected]"], "hi") == 3
    assert n.sent == [("[email protected]", "hi"), ("[email protected]", "hi")]
    assert spy.call_count == 2
    assert spy.spy_return == 2
    spy.assert_called_with("[email protected]", "hi")

def test_patch_object_stubs_return_and_args(mocker):
    n = Notifier()
    mock_send = mocker.patch.object(n, "send", return_value=42)
    assert broadcast(n, ["[email protected]"], "hi") == 42
    mock_send.assert_called_once_with("[email protected]", "hi")
    assert n.sent == []

def test_patch_context_manager_and_call_args(mocker):
    smtp_cls = mocker.patch("service.smtplib.SMTP", autospec=True)
    smtp = smtp_cls.return_value.__enter__.return_value
    assert send_via_smtp("mail.local", 2525, "[email protected]", "hi")
    smtp_cls.assert_called_once_with("mail.local", 2525, timeout=5)
    smtp.sendmail.assert_called_once_with("[email protected]", ["[email protected]"], "hi")
pytest.ini
[pytest]
pythonpath = .

Connect to ProvenPin

Add the free MCP server so your agent can pull this recipe and others like it. Same setup as the docs.

terminal
claude mcp add --transport http provenpin https://mcp.provenpin.com/mcp

Any other MCP client: Streamable HTTP at https://mcp.provenpin.com/mcp. No API key, no signup.