web-dev-qa-db-fra.com

Mocking ouvert (nom_fichier) dans les tests unitaires

J'ai un code source qui ouvre un fichier csv et configure un en-tête pour l'association de valeur . Le code source est donné ci-dessous: 

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

J'essaie de me moquer de la déclaration suivante 

rack_type_file = open(rack_file) 

Comment puis-je simuler une fonction ouverte (...)?

22
Kartik

Il s’agit certes d’une vieille question, c’est pourquoi certaines des réponses sont obsolètes.

Dans la version actuelle de la bibliothèque mock, une fonction de commodité est conçue précisément à cette fin. Voici comment ça fonctionne:

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

La documentation est ici .

20
mac

Pour simuler une fonction intégrée ouverte avec mox, utilisez le module __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()

Notez que __builtins__ n'est pas toujours un module, il peut être de type dict. Veuillez utiliser le module __builtin__ (sans "s") pour faire référence aux méthodes intégrées du système.

En savoir plus sur le module __builtin__: http://docs.python.org/library/ builtin . Html

13
Lukas Normantas

J'aime le faire de deux manières, selon la situation.

Si votre test unitaire appelle directement ParseCsvFile, je voudrais ajouter un nouveau kwarg à ParseCsvFile:

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

Ensuite, votre test unitaire peut passer un autre open_func afin de réaliser le moquage.

Si votre test unitaire appelle une autre fonction qui, à son tour, appelle ParseCsvFile, il est moche de contourner open_func uniquement pour des tests. Dans ce cas, j'utiliserais le module mock . Cela vous permet de modifier une fonction par son nom et de la remplacer par un objet factice.

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

Est simple avec un décorateur (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')]
4
wierzbiks

J'ai pris la liberté de ré-écrire votre fonction exemple:

Supposons que votre fonction se trouve dans un fichier nommé 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

Un cas de test simple serait:

# 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 __== '__main__':
    unittest.main()

MODIFIER:

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

Fonctionne bien sur 2.6 en utilisant mox 0,53

3

Bonjour, je rencontrais un problème similaire, et je me suis déchiré les cheveux en basculant entre différentes bibliothèques moqueuses. J'ai finalement trouvé une solution qui me convient et qui pourrait peut-être vous aider? Finalement, je suis allé à la bibliothèque Mocker http://labix.org/mocker et voici le code permettant de se moquer:

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

Incidemment, la raison pour laquelle je suis allé à Mocker est parce que je testais une fonction qui utilisait open pour lire un fichier, puis utilisée open pour écraser le même fichier avec de nouvelles données. Ce qu'il me fallait pour pouvoir faire, c’était tester le cas où le fichier initial n’existait pas. Préparez donc une maquette qui jetterait un IOError la première fois puis fonctionnerait une seconde fois. La configuration pour laquelle ressemblait à ceci:

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

J'espère que cela t'aides!

2
Ctrlspc
>>> 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>
0
Ben

@ mock.patch decorator (exemple 2.7)

C'est maintenant beaucoup plus facile:

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
Jeremy Swagger