Подтвердить что ты не робот

Стыковка открытого файла (имя_файла) в модульных тестах

У меня есть исходный код, который открывает файл 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 (...)?

4b9b3361

Ответ 1

Это, по общему признанию, старый вопрос, поэтому некоторые из ответов устарели.

В текущей версии библиотеки 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')

Документация здесь.

Ответ 2

Чтобы высмеять встроенную функцию open с использованием модуля 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__ (без модуля "s" ) для ссылки на встроенные методы системы.

Подробнее о модуле __builtin__: http://docs.python.org/library/ встроенный.html

Ответ 3

Есть два способа, которые мне нравятся, в зависимости от ситуации.

Если ваш unit test будет вызывать ParseCsvFile напрямую, я бы добавил новый kwarg в ParseCsvFile:

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

Затем ваш unit test может передать другой open_func, чтобы выполнить насмешку.

Если ваш unit test вызывает некоторую другую функцию, которая, в свою очередь, вызывает ParseCsvFile, тогда прохождение open_func только для тестов является уродливым. В этом случае я использовал модуль mock. Это позволяет вам изменять функцию по имени и заменять ее на объект 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")
        # ...

Ответ 4

Я взял на себя смелость переписать твою функцию:

Предположим, что ваша функция находится в файле с именем 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()

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

Хорошо работает на 2.6 с использованием мокса 0.53

Ответ 5

Прост с декоратором (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')]

Ответ 6

Привет, у меня была аналогичная проблема, и я разорвал свои волосы, перекидываясь между разными насмешливыми библиотеками. Наконец-то я нашел решение, которым я доволен, и, может быть, это может вам помочь? В конце я пошел с библиотекой Mocker http://labix.org/mocker, и вот код для насмешливого открытия:

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, заключается в том, что я тестировал функцию, которая открывалась для чтения файла, а затем снова использовалась для перезаписывания того же файла новыми данными. То, что мне нужно было сделать, это проверить случай, когда исходный файл не существует, поэтому настроил макет, который впервые ввел 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()

Надеюсь, это поможет!

Ответ 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
<open file 'test.py', mode 'r' at 0xb7736230>