Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Python: Mocking a context manager

Tags:

python

mocking

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' 
like image 254
Willem Avatar asked Mar 04 '15 08:03

Willem


People also ask

What is context manager in Python?

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.

What is Mocker in Python?

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 you mock a function in Python?

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 .

What is @patch in Python?

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.


2 Answers

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.

like image 153
Michele d'Amico Avatar answered Sep 21 '22 11:09

Michele d'Amico


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 
like image 35
FiddleStix Avatar answered Sep 22 '22 11:09

FiddleStix