Skip to content

fix(tests): don't stomp real mcp with MagicMocks when mcp is installed#309

Open
breedx wants to merge 2 commits intompfaffenberger:mainfrom
breedx:fix/tests-conftest-mcp-mock
Open

fix(tests): don't stomp real mcp with MagicMocks when mcp is installed#309
breedx wants to merge 2 commits intompfaffenberger:mainfrom
breedx:fix/tests-conftest-mcp-mock

Conversation

@breedx
Copy link
Copy Markdown
Contributor

@breedx breedx commented Apr 27, 2026

Problem

Running any two test files together where one is under `tests/plugins/` and another transitively imports `pydantic_ai.mcp` fails during collection:

```
ModuleNotFoundError: No module named 'mcp.client.sse'; 'mcp.client' is not a package
```

Reproducer (requires `pydantic-ai-slim[mcp]` to be installed — it is, via the main dependency):

```bash
uv run pytest tests/agents/test_compaction.py tests/plugins/test_azure_foundry.py -q

→ ERROR at collection, 1 error

```

Run either file on its own and it passes.

Root cause

`tests/plugins/conftest.py::pytest_configure` unconditionally installs `MagicMock`s into `sys.modules` for `mcp`, `mcp.client`, and `mcp.client.session` whenever `"mcp" not in sys.modules`. Pytest starts with `mcp` not yet imported, so the mock path always wins — even when the real `mcp` package is installed.

Then when collection reaches a test module that transitively imports `pydantic_ai.mcp` (e.g. via `code_puppy.agents._builder` → `pydantic_ai.durable_exec.dbos`), the line `from mcp.client.sse import sse_client` tries to look up `.sse` as a submodule of the MagicMock-`mcp.client`, which has no `path`, and Python raises "mcp.client is not a package". That gets re-raised as the helpful "please install mcp" error.

Single-file runs of either affected test worked because `tests/plugins/conftest.py` wasn't loaded for them.

Fix

Probe with a real import of `mcp` / `mcp.types` first. Only install the mocks if the real import fails. Real dev envs (including this repo's own) skip the mocks entirely; bare CI images without `mcp` still get the compatibility shim.

Test plan

  • `uv run pytest tests/agents/test_compaction.py tests/plugins/test_azure_foundry.py -q --no-cov` → 99 pass (was: collection error)
  • `uv run pytest tests/plugins/test_azure_foundry.py tests/plugins/test_aws_bedrock.py -q --no-cov` → 103 pass (no regression on plugin-only runs)
  • `ruff format --check` + `ruff check` clean

breedx added 2 commits April 27, 2026 17:18
tests/plugins/conftest.py unconditionally installed MagicMocks into
sys.modules['mcp'], 'mcp.client', and 'mcp.client.session' whenever
'mcp' wasn't already imported. Real mcp is in the dev env, but pytest
starts with it not yet imported, so the mock path always won. Later,
when tests like tests/agents/test_compaction.py transitively imported
pydantic_ai.mcp, its 'from mcp.client.sse import sse_client' blew up
with:

  ModuleNotFoundError: No module named 'mcp.client.sse';
    'mcp.client' is not a package

because the installed MagicMock had no __path__.

Two-file pytest invocations spanning tests/plugins/* plus any module
that touches pydantic_ai.mcp would fail collection. Single-file runs
of either worked because tests/plugins/conftest.py wasn't loaded.

Fix: try importing real mcp first, only install the mocks when the
import fails. Real dev envs skip the mock entirely; bare CI images
without mcp still get the compatibility shim.
CI installs unpinned ruff, which now wraps the negated isinstance
lambda differently.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant