在单元测试中模拟 open(file_name)

发布于 2024-10-21 01:44:41 字数 1212 浏览 3 评论 0原文

我有一个源代码,可以打开 csv 文件并设置标题 价值关联。源代码如下:

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() 

我正在尝试模拟以下语句

rack_type_file = open(rack_file) 

如何模拟 open(...) 函数?

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?

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(8

海夕 2024-10-28 01:44:41

诚然,这是一个老问题,因此一些答案已经过时了。

在当前版本的mock 库中有一个专门为此目的而设计的便利函数。它的工作原理如下:

>>> 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')

文档位于此处

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.

忘羡 2024-10-28 01:44:41

要模拟使用 mox 打开的内置函数,请使用 __builtin__ 模块:

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()

请注意,__builtins__ 并不总是模块,它可以是 dict 类型,请使用 __builtin__< /code>(不带“s”)模块引用系统内置方法。

有关 __builtin__ 模块的更多信息:http://docs.python.org/library /内置.html

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

初见终念 2024-10-28 01:44:41

我喜欢用两种方法来做到这一点,具体取决于具体情况。

如果您的单元测试要直接调用 ParseCsvFile,我会向 ParseCsvFile 添加一个新的 kwarg:

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

然后您的单元测试可以传递不同的 open_func 以完成模拟。

如果您的单元测试调用其他一些函数,而这些函数又调用 ParseCsvFile,那么仅为了测试而传递 open_func 是很丑陋的。在这种情况下,我将使用 模拟模块。这使您可以按名称更改函数并将其替换为 Mock 对象。

# 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")
        # ...

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")
        # ...
今天小雨转甜 2024-10-28 01:44:41

使用装饰器(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')]

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')]
去了角落 2024-10-28 01:44:41

我冒昧地重写了您的示例函数:

假设您的函数位于名为 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

一个简单的测试用例是:

# 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()

编辑:

% /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()

使用 mox 0.53 在 2.6 上运行良好

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

撩起发的微风 2024-10-28 01:44:41

嗨,我遇到了类似的问题,并且在不同的模拟库之间翻来覆去地撕扯着我的头发。我终于找到了一个令我满意的解决方案,也许它可以帮助你?最后我选择了 Mocker 库 http://labix.org/mocker 这里是模拟的代码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'))

<the rest of your test setup goes here>

mocker.replay()

ParseCsvFile(sourceFile)

mocker.restore()
mocker.verify()

顺便说一句,我使用 Mocker 的原因是因为我正在测试一个使用 open 读取文件的函数,然后再次使用 open 用新数据覆盖同一文件。我需要做的是测试初始文件不存在的情况,因此设置一个模拟,第一次抛出 IOError,然后第二次工作。其设置如下所示:

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)

<rest of test setup >

mocker.replay()

<test>

mocker.restore() #required to restore the open method
mocker.verify()

希望这有帮助!

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'))

<the rest of your test setup goes here>

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)

<rest of test setup >

mocker.replay()

<test>

mocker.restore() #required to restore the open method
mocker.verify()

Hope this helps!

残龙傲雪 2024-10-28 01:44:41

@mock.patch 装饰器(2.7 示例)

现在更容易了:

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)

@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)
甜`诱少女 2024-10-28 01:44:41
>>> 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
<open file 'test.py', mode 'r' at 0xb7736230>
>>> 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
<open file 'test.py', mode 'r' at 0xb7736230>
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文