Skip to content
Back to skills

Python Mock Patching Location

ASecurity

Python mock.patch must target the import location, not the definition location

  • 4 stars
  • 0 votes
  • 0 copies
  • 1 view
  • Added September 3, 2026
ai-agentspythongotestingdebugging

Works with

  • cli

Security analysis

A100/100

Scanned September 3, 2026

npx -y skills add fabioc-aloha/Alex_Skill_Mall --skill python-mock-patching-location --agent claude-code

Installs into .claude/skills of the current project.

Are you the author of Python Mock Patching Location?

Add the live security badge to your README. It updates with every re-scan.

Security grade badge for Python Mock Patching Location
[![Security: A — Skills Directory](https://www.skillsdirectory.com/api/skills/fabioc-aloha-python-mock-patching-location/badge)](https://www.skillsdirectory.com/skills/fabioc-aloha-python-mock-patching-location)

More formats (shields.io, HTML) on the badges page. Keep it an A: scan every change in CI with Pro.

Download with Pro
SKILL.md
---
name: python-mock-patching-location
description: "Python mock.patch must target the import location, not the definition location"
lastReviewed: 2026-04-30
---

# Python Mock Patching Location

**Category**: Testing
**Time Saved**: 30+ minutes debugging "mock not working"
**Battle-tested**: Yes — ChessCoach, multiple Python projects

---

## The Problem

You're writing a test that needs to mock `get_client()` from `module_a`. You patch `module_a.get_client`. The mock doesn't work — the real function still runs.

## Why It Happens

Python's import system creates a **reference** to the function in the importing module. When `module_b` does `from module_a import get_client`, it creates `module_b.get_client` as a separate reference. Patching the original doesn't affect the copy.

## The Rule

**Patch where the function is USED, not where it's DEFINED**

```python
# module_a.py defines get_client()
# module_b.py imports and calls it

# ❌ WRONG — patches the definition, not the usage
@patch('module_a.get_client')
def test_something(mock_client):
    result = module_b.do_work()  # Still calls real get_client!

# ✅ CORRECT — patches where it's called
@patch('module_b.get_client')
def test_something(mock_client):
    result = module_b.do_work()  # Uses mock
```

## Decision Table

| Import Style in Target Module | Patch Target |
|-------------------------------|--------------|
| `from module_a import func` | `target_module.func` |
| `import module_a` then `module_a.func()` | `module_a.func` |
| `from module_a import func as alias` | `target_module.alias` |

## Async Context Manager Trap

`MagicMock` doesn't support `async with` by default:

```python
# ❌ FAILS — MagicMock can't handle async context manager
mock_lock = MagicMock()
async with mock_lock:  # TypeError

# ✅ WORKS — proper async mock
class MockAsyncLock:
    async def __aenter__(self):
        return self
    async def __aexit__(self, *args):
        pass

mock_lock = MockAsyncLock()
```

## Verification

Test passes AND you've confirmed the mock was actually called:

```python
@patch('module_b.get_client')
def test_something(mock_client):
    mock_client.return_value = fake_response
    result = module_b.do_work()
    
    mock_client.assert_called_once()  # Verify mock was hit
```

If `assert_called_once()` fails with "Expected call not found", you patched the wrong location.

---

**Source**: Promoted from AI-Memory global-knowledge.md (2026-04-27)

Attribution

Is this your skill, or is something wrong with this listing? Request removal or report an issue. Author removals are honored within 72 hours.

Comments

Loading comments…

Newsletter

Get the weekly list of safe new skills, and the ones we pulled.