可以将文章内容翻译成中文,广告屏蔽插件可能会导致该功能失效(如失效,请关闭广告屏蔽插件后再试):
问题:
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?
回答1:
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.
回答2:
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
回答3:
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") # ...
回答4:
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')]
回答5:
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
回答6:
Hi I was having a similar problem, and was tearing my hair out flipping between different mocking libraries. I finally found a solution that I am happy with, and maybe it might help you? In the end I went with the Mocker library http://labix.org/mocker and here is the code for mocking open:
from mocker import Mocker from StringIO import StringIO import __builtin__ mocker = Mocker() sourceFile = 'myTestFile.txt' __builtin__.open = mocker.mock() __builtin__.open(sourceFile) mocker.result(StringIO('this,is,a,test,file')) mocker.replay() ParseCsvFile(sourceFile) mocker.restore() mocker.verify()
Incidentaly the reason I went with Mocker is because I was testing a function which used open to read a file, and then used open again to overwrite the same file with new data. What I needed to be able to do was test the case where the initial file didn't exist, so set up a mock, that threw an IOError the first time, and then worked the second time. The setup for which looked like this:
from mocker import Mocker import __builtin__ mocker = Mocker() mockFileObject = mocker.mock() __builtin__.open = mocker.mock() __builtin__.open('previousState.pkl', 'r') mocker.throw(IOError('Boom')) __builtin__.open('previousState.pkl','w') mocker.result(mockFileObject) mocker.replay() mocker.restore() #required to restore the open method mocker.verify()
Hope this helps!
回答7:
>>> class A(object): ... def __init__(self): ... self.x = open('test.py') ... >>> old_open = open >>> def open(s): ... return "test\n" ... >>> a = A() >>> a.x 'test\n' >>> open = old_open >>> a = A() >>> a.x
回答8:
@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)