복붙노트

[PYTHON] 단위 테스트에서 open (file_name) 조롱

PYTHON

단위 테스트에서 open (file_name) 조롱

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) 

오픈 (...) 기능을 어떻게 조롱합니까?

해결법

  1. ==============================

    1.이것은 낡은 질문이기 때문에 일부 답변이 오래된 것입니다.

    이것은 낡은 질문이기 때문에 일부 답변이 오래된 것입니다.

    모의 라이브러리의 현재 버전에는 정확히이 목적을 위해 설계된 편의 함수가 있습니다. 다음은 작동 방식입니다.

    >>> 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. ==============================

    2.mox를 사용하여 내장 함수를 모의 사용하려면 __builtin__ module을 사용하십시오.

    mox를 사용하여 내장 함수를 모의 사용하려면 __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()
    

    __builtins__은 항상 모듈이 아니며 dict 유형일 수 있습니다. 시스템 내장 메서드를 참조하려면 __builtin__ ( "s"없음) 모듈을 사용하십시오.

    __builtin__ 모듈에 대한 추가 정보 : http://docs.python.org/library/builtin.html

  3. ==============================

    3.상황에 따라이 작업을 수행하는 두 가지 방법이 있습니다.

    상황에 따라이 작업을 수행하는 두 가지 방법이 있습니다.

    만약 당신의 유닛 테스트가 ParseCsvFile을 직접 호출한다면 나는 ParseCsvFile에 새로운 kwarg를 추가 할 것입니다 :

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

    그런 다음 단위 테스트는 조롱을 달성하기 위해 다른 open_func를 전달할 수 있습니다.

    유닛 테스트가 ParseCsvFile을 호출하는 다른 함수를 호출하면 테스트를 위해 open_func를 전달하는 것은 추악합니다. 이 경우 모의 모듈을 사용할 것입니다. 이를 통해 함수를 이름으로 변경하고 모의 객체로 대체 할 수 있습니다.

    # 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. ==============================

    4.데코레이터 (Python3)로 간단합니다 :

    데코레이터 (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. ==============================

    5.나는 샘플 함수를 다시 쓰는 자유를 얻었다.

    나는 샘플 함수를 다시 쓰는 자유를 얻었다.

    함수가 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에서 정상적으로 작동합니다.

  6. ==============================

    6.안녕 비슷한 문제가 있었는데, 다른 mocking 라이브러리 사이에 내 머리카락이 튀어 나와 밖으로 찢어지고 있었다. 마침내 내가 만족스럽고 어쩌면 도움이 될만한 해결책을 찾았습니까? 결국 나는 Mocker 라이브러리 http://labix.org/mocker와 함께 갔고, 여기에 조롱을위한 코드가있다 :

    안녕 비슷한 문제가 있었는데, 다른 mocking 라이브러리 사이에 내 머리카락이 튀어 나와 밖으로 찢어지고 있었다. 마침내 내가 만족스럽고 어쩌면 도움이 될만한 해결책을 찾았습니까? 결국 나는 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()
    

    Incidentaly 내가 Mocker와 함께 갔던 이유는 파일을 읽기 위해 열린 함수를 테스트하고 있었기 때문에 같은 파일을 새로운 데이터로 덮어 쓰는 데 다시 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()
    

    희망이 도움이!

  7. ==============================

    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>
    
  8. ==============================

    8.@ mock.patch 데코레이터 (2.7 예)

    @ 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)
    
  9. from https://stackoverflow.com/questions/5237693/mocking-openfile-name-in-unit-tests by cc-by-sa and MIT license