I don't understand why I can't mock NamedTemporaryFile.name in this example:
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)
Test results in:
AssertionError: <MagicMock name='NamedTemporaryFile().__enter__().name' id='140275675011280'> != 'abcde'
Context managers are used to set up and tear down temporary contexts, establish and resolve custom settings, and acquire and release resources. The open() function for opening files is one of the most familiar examples of a context manager.
unittest.mock is a library for testing in Python. It allows you to replace parts of your system under test with mock objects and make assertions about how they have been used. unittest.mock provides a core Mock class removing the need to create a host of stubs throughout your test suite.
How do we mock in Python? Mocking in Python is done by using patch to hijack an API function or object creation call. When patch intercepts a call, it returns a MagicMock object by default. By setting properties on the MagicMock object, you can mock the API call to return any value you want or raise an Exception .
This, along with its subclasses, will meet most Python mocking needs that you will face in your tests. The library also provides a function, called patch() , which replaces the real objects in your code with Mock instances.
You are setting the wrong mock: mock_tmp
is not the context manager, but instead returns a context manager. Replace your setup line with:
mock_tmp.return_value.__enter__.return_value.name = mytmpname
and your test will work.
To expand on Nathaniel's answer, this code block
with tempfile.NamedTemporaryFile() as mytmp: return mytmp.name
effectively does three things
# Firstly, it calls NamedTemporaryFile, to create a new instance of the class. context_manager = tempfile.NamedTemporaryFile() # Secondly, it calls __enter__ on the context manager instance. mytmp = context_manager.__enter__() # Thirdly, we are now "inside" the context and can do some work. return mytmp.name
When you replace tempfile.NamedTemporaryFile
with an instance of Mock
or MagicMock
context_manager = mock_tmp() # This first line, above, will call mock_tmp(). # Therefore we need to set the return_value with # mock_tmp.return_value mytmp = context_manager.__enter__() # This will call mock_tmp.return_value.__enter__() so we need to set # mock_tmp.return_value.__enter__.return_value return mytmp.name # This will access mock_tmp.return_value.__enter__.return_value.name
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With