Mocking open(file_name) in unit tests

前端 未结 8 1811
慢半拍i
慢半拍i 2020-12-05 00:37

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): 
  \"\"\"Pa         


        
相关标签:
8条回答
  • 2020-12-05 00:57

    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")
            # ...
    
    0 讨论(0)
  • 2020-12-05 01:00

    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.

    0 讨论(0)
  • 2020-12-05 01:01

    Is simple with decorator (Python3):

    def my_method():
        with open(file="/1.txt", mode='r', encoding='utf-8') as file:
            return file.read().strip()
    
    
    @mock.patch("builtins.open", create=True)
    def test_my_method(mock_open):
        mock_open.side_effect = [
            mock.mock_open(read_data="A").return_value
        ]
    
        resA = my_method()
        assert resA == "A"
    
        mock_open.mock_calls ==  [mock.call(file="/1.txt", mode='r', encoding='utf-8')]
    
    0 讨论(0)
  • 2020-12-05 01:01

    @mock.patch decorator (2.7 example)

    This is now much easier:

    import your_script.py
    import __builtin__
    import mock
    
    
    @mock.patch("__builtin__.open")
    def test_example(self, mock_open):
        your_script.your_method()
        self.assertEqual(mock_open.call_count, 1)
    
    0 讨论(0)
  • 2020-12-05 01:04

    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

    0 讨论(0)
  • 2020-12-05 01:07

    I took the liberty of re-writing your sample function:

    Assume your function is located in a file named code.py

    # code.py
    import csv
    
    import logging
    
    
    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_file = source
        attributes_list = []
    
        try:
            rack_type_file = open(rack_file)
        except IOError, (errno, strerror):
            logging.error("I/O error(%s): %s", errno, strerror)
        else:
            reader = csv.DictReader(rack_type_file, delimiter=',')
            attributes_list = [line for line in reader]   # list of dictionaries
            rack_type_file.close()
    
        return attributes_list
    

    A simple test case would be:

    # your test file
    import __builtin__
    import unittest
    import contextlib
    from StringIO import StringIO
    
    import mox
    
    import code
    
    
    @contextlib.contextmanager
    def mox_replayer(mox_instance):
        mox_instance.ReplayAll()
        yield
        mox_instance.VerifyAll()
    
    
    class TestParseCSVFile(unittest.TestCase):
    
        def setUp(self):
            self.mox = mox.Mox()
    
        def tearDown(self):
            self.mox.UnsetStubs()
    
        def test_parse_csv_file_returns_list_of_dicts(self):
            TEST_FILE_NAME = 'foo.csv'
    
            self.mox.StubOutWithMock(__builtin__, 'open')
            open(TEST_FILE_NAME).AndReturn(StringIO("name,age\nfoo,13"))
    
            with mox_replayer(self.mox):
                result = code.ParseCsvFile(TEST_FILE_NAME)
    
            self.assertEqual(result, [{'age': '13', 'name': 'foo'}])  # works!
    
    
    if __name__ == '__main__':
        unittest.main()
    

    EDIT:

    % /usr/bin/python2.6
    Python 2.6.1 (r261:67515, Jun 24 2010, 21:47:49) 
    [GCC 4.2.1 (Apple Inc. build 5646)] on darwin
    Type "help", "copyright", "credits" or "license" for more information.
    >>> import __builtin__
    >>> import mox
    >>> mock = mox.Mox()
    >>> mock.StubOutWithMock(__builtin__, 'open')
    >>> mock.UnsetStubs()
    

    Works fine on 2.6 using mox 0.53

    0 讨论(0)
提交回复
热议问题