Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Mocking open(file_name) in unit tests [duplicate]

I have a source code that opens a csv file and sets up a header to value association. The source code is given below:

def ParseCsvFile(source): 
  """Parse the csv file. 
  Args: 
    source: file to be parsed

  Returns: the list of dictionary entities; each dictionary contains
             attribute to value mapping or its equivalent. 
  """ 
  global rack_file 
  rack_type_file = None 
  try: 
    rack_file = source 
    rack_type_file = open(rack_file)  # Need to mock this line.
    headers = rack_type_file.readline().split(',') 
    length = len(headers) 
    reader = csv.reader(rack_type_file, delimiter=',') 
    attributes_list=[] # list of dictionaries. 
    for line in reader: 
      # More process to happeng. Converting the rack name to sequence. 
      attributes_list.append(dict((headers[i],
                                   line[i]) for i in range(length))) 
    return attributes_list 
  except IOError, (errno, strerror): 
    logging.error("I/O error(%s): %s" % (errno, strerror)) 
  except IndexError, (errno, strerror): 
    logging.error('Index Error(%s), %s' %(errno, strerror)) 
  finally: 
    rack_type_file.close() 

I am trying to mock the following statement

rack_type_file = open(rack_file) 

How do I mock open(...) function?

like image 268
Kartik Avatar asked Mar 08 '11 20:03

Kartik


People also ask

What can be mocked for unit testing?

What is mocking? Mocking is a process used in unit testing when the unit being tested has external dependencies. The purpose of mocking is to isolate and focus on the code being tested and not on the behavior or state of external dependencies.

How do you mock a file in Unittest Python?

If your unit test is going to call ParseCsvFile directly I would add a new kwarg to ParseCsvFile: def ParseCsvFile(source, open=open): # ... rack_type_file = open(rack_file) # Need to mock this line. Then your unit test can pass a different open_func in order to accomplish the mocking.

Should unit tests use mocks?

In a unit test, mock objects can simulate the behavior of complex, real objects and are therefore useful when it is impractical or impossible to incorporate a real object into a unit test. Mocking makes sense in a unit testing context.

What is mocking in unit testing Python?

What is mocking in Python? Mocking in Python means the unittest. mock library is being utilized to replace parts of the system with mock objects, allowing easier and more efficient unit testing than would otherwise be possible.


3 Answers

This is admittedly an old question, hence some of the answers are outdated.

In the current version of the mock library there is a convenience function designed for precisely this purpose. Here's how it works:

>>> from mock import mock_open
>>> m = mock_open()
>>> with patch('__main__.open', m, create=True):
...     with open('foo', 'w') as h:
...         h.write('some stuff')
...
>>> m.mock_calls
[call('foo', 'w'),
 call().__enter__(),
 call().write('some stuff'),
 call().__exit__(None, None, None)]
>>> m.assert_called_once_with('foo', 'w')
>>> handle = m()
>>> handle.write.assert_called_once_with('some stuff')

Documentation is here.

like image 115
mac Avatar answered Oct 19 '22 20:10

mac


To mock built-in function open with mox use __builtin__ module:

import __builtin__ # unlike __builtins__ this must be imported
m = mox.Mox()
m.StubOutWithMock(__builtin__, 'open')
open('ftphelp.yml', 'rb').AndReturn(StringIO("fake file content"))     
m.ReplayAll()
# call the code you want to test that calls `open`
m.VerifyAll()
m.UnsetStubs()

Note that __builtins__ is not always a module, it can be of type dict, please use __builtin__ (with no "s") module to refer to system built-in methods.

More about __builtin__ module: http://docs.python.org/library/builtin.html

like image 28
Luke 10X Avatar answered Oct 19 '22 21:10

Luke 10X


There are two ways that I like to do this, depending on the situation.

If your unit test is going to call ParseCsvFile directly I would add a new kwarg to ParseCsvFile:

def ParseCsvFile(source, open=open): 
    # ...
    rack_type_file = open(rack_file)  # Need to mock this line.

Then your unit test can pass a different open_func in order to accomplish the mocking.

If your unit test calls some other function that in turn calls ParseCsvFile then passing around open_func just for tests is ugly. In that case I would use the mock module. This lets you alter a function by name and replace it with a Mock object.

# code.py
def open_func(name):
    return open(name)

def ParseCsvFile(source):
    # ...
    rack_type_file = open_func(rack_file)  # Need to mock this line.

# test.py
import unittest
import mock
from StringIO import StringIO

@mock.patch('code.open_func')
class ParseCsvTest(unittest.TestCase):
    def test_parse(self, open_mock):
        open_mock.return_value = StringIO("my,example,input")
        # ...
like image 11
Spike Gronim Avatar answered Oct 19 '22 19:10

Spike Gronim