python runtime_error ai_generated true

AssertionError: Expected 'send_email' to be called once. Called 0 times.

ID: python/mock-patch-wrong-target

Also available as: JSON · Markdown · 中文
80%Fix Rate
89%Confidence
0Evidence
2024-01-19First Seen

Version Compatibility

VersionStatusIntroducedDeprecatedNotes
3.11 active — — —
3.12 active — — —

Root Cause

unittest.mock.patch targeted the module where the function is defined, but the code under test imported it by name into its own namespace. The patch replaces the original symbol, not the already-imported reference.

generic

中文

unittest.mock.patch 打补丁的对象是函数定义所在的模块,但被测代码通过 from ... import 把该函数导入了自己的命名空间。补丁替换的是原始符号,而非已导入的引用。

Workarounds

  1. 94% success
    # app/service.py: from app.mail import send_email
    def notify(user):
        send_email(user.email)
    
    # test_service.py
    from unittest.mock import patch
    @patch('app.service.send_email')
    def test_notify(mock_send):
        notify(user)
        mock_send.assert_called_once()
  2. 92% success
    def test_notify(mocker):
        m = mocker.patch('app.service.send_email')
        notify(user)
        m.assert_called_once_with('[email protected]')

Dead Ends

Common approaches that don't work:

  1. 80% fail

    The real function is being called, not the mock. Changing call counts doesn't make the patch take effect.

  2. 70% fail

    autospec validates the signature but still patches the wrong namespace; the code under test still holds the original reference.