Python: Издевается над менеджером контекста

Я не понимаю, почему я не могу издеваться над NamedTemporaryFile.name в этом примере:

from mock import Mock, patch
import unittest
import tempfile

def myfunc():
    with tempfile.NamedTemporaryFile() as mytmp:
        return mytmp.name

class TestMock(unittest.TestCase):
    @patch('tempfile.NamedTemporaryFile')
    def test_cm(self, mock_tmp):
        mytmpname = 'abcde'
        mock_tmp.__enter__.return_value.name = mytmpname
        self.assertEqual(myfunc(), mytmpname)

Результаты тестов:

AssertionError: <MagicMock name='NamedTemporaryFile().__enter__().name' id='140275675011280'> != 'abcde'

Ответы

Ответ 1

Вы устанавливаете неправильный макет: mock_tmp не является менеджером контекста, но вместо этого возвращает менеджер контекста. Замените линию настройки следующим образом:

mock_tmp.return_value.__enter__.return_value.name = mytmpname

и ваш тест будет работать.

Ответ 2

Вот альтернативный вариант с pytest и mocker fixa, что также является обычной практикой:

def test_myfunc(mocker):
    mock_tempfile = mocker.MagicMock(name='tempfile')
    mocker.patch(__name__ + '.tempfile', new=mock_tempfile)
    mytmpname = 'abcde'
    mock_tempfile.NamedTemporaryFile.return_value.__enter__.return_value.name = mytmpname
    assert myfunc() == mytmpname

Ответ 3

Расширяя ответ петра К, используя pytest и крепление насмешника.

def myfunc():
    with tempfile.NamedTemporaryFile(prefix='fileprefix') as fh:
        return fh.name


def test_myfunc(mocker):
    mocker.patch('tempfile.NamedTemporaryFile').return_value.__enter__.return_value.name = 'tempfilename'
    assert myfunc() == 'tempfilename'