このコードを処理するには、unittest.mock.mock_openをどのようにカスタマイズする必要がありますか?
file: impexpdemo.py
def import_register(register_fn):
with open(register_fn) as f:
return [line for line in f]
私の最初の試みはread_data
を試しました。
class TestByteOrderMark1(unittest.TestCase):
REGISTER_FN = 'test_dummy_path'
TEST_TEXT = ['test text 1\n', 'test text 2\n']
def test_byte_order_mark_absent(self):
m = unittest.mock.mock_open(read_data=self.TEST_TEXT)
with unittest.mock.patch('builtins.open', m):
result = impexpdemo.import_register(self.REGISTER_FN)
self.assertEqual(result, self.TEST_TEXT)
これは失敗しました。おそらく、コードがread、readline、またはreadlinesを使用していないためです。 unittest.mock.mock_openの documentation は、「read_dataは、ファイルハンドルのread()、readline()、およびreadlines()メソッドが返す文字列です。これらのメソッドの呼び出しには時間がかかります。 read_dataから枯渇するまでのデータ。これらのメソッドのモックはかなり単純です。テストされたコードにフィードするデータをさらに制御する必要がある場合は、このモックを自分でカスタマイズする必要があります。read_dataはデフォルトで空の文字列です。 。」
ドキュメントにはどのようなカスタマイズが必要かについてのヒントがないので、私はreturn_value
とside_effect
を試しました。どちらも機能しませんでした。
class TestByteOrderMark2(unittest.TestCase):
REGISTER_FN = 'test_dummy_path'
TEST_TEXT = ['test text 1\n', 'test text 2\n']
def test_byte_order_mark_absent(self):
m = unittest.mock.mock_open()
m().side_effect = self.TEST_TEXT
with unittest.mock.patch('builtins.open', m):
result = impexpdemo.import_register(self.REGISTER_FN)
self.assertEqual(result, self.TEST_TEXT)
mock_open()
オブジェクトは実際には反復を実装していません。
ファイルオブジェクトをコンテキストマネージャーとして使用していない場合は、次を使用できます。
_m = unittest.mock.MagicMock(name='open', spec=open)
m.return_value = iter(self.TEST_TEXT)
with unittest.mock.patch('builtins.open', m):
_
これで、open()
はイテレータを返します。これは、ファイルオブジェクトと同じように直接反復できるものであり、next()
でも機能します。ただし、コンテキストマネージャーとして使用することはできません。
これをmock_open()
と組み合わせて、戻り値に___iter__
_および___next__
_メソッドを提供できます。さらに、mock_open()
が使用の前提条件を追加するという利点もあります。コンテキストマネージャーとして:
_# Note: read_data must be a string!
m = unittest.mock.mock_open(read_data=''.join(self.TEST_TEXT))
m.return_value.__iter__ = lambda self: self
m.return_value.__next__ = lambda self: next(iter(self.readline, ''))
_
ここでの戻り値は、MagicMock
オブジェクト(Python 2)または メモリ内ファイルオブジェクト (Python 3)から指定されたfile
オブジェクトですが、read
、write
、および___enter__
_メソッドのみがあります。スタブアウトされました。
上記はPython 2では機能しません。a)Python 2は___next__
_ではなくnext
が存在することを期待し、b)next
は処理されないためです。 Mockの特別なメソッドとして(当然のことながら)、上記の例で___next__
_の名前をnext
に変更した場合でも、戻り値のtype値にはnext
メソッドはありません。 mostの場合、ファイルオブジェクトがiterableを生成するようにするだけで十分です。 )イテレータではなく:
_# Python 2!
m = mock.mock_open(read_data=''.join(self.TEST_TEXT))
m.return_value.__iter__ = lambda self: iter(self.readline, '')
_
iter(fileobj)
を使用するコードはすべて機能します(for
ループを含む)。
このギャップを修正することを目的とした Pythonトラッカー の未解決の問題があります。
Python 3.6の時点で、unittest.mock_open
メソッドによって返されるモックファイルのようなオブジェクト 反復をサポートしていません 。このバグは2014年に報告され、 2017年現在も営業しています。
したがって、このようなコードはサイレントにゼロ反復を生成します。
f_open = unittest.mock.mock_open(read_data='foo\nbar\n')
f = f_open('blah')
for line in f:
print(line)
適切な行イテレータを返すモックオブジェクトにメソッドを追加することで、この制限を回避できます。
def mock_open(*args, **kargs):
f_open = unittest.mock.mock_open(*args, **kargs)
f_open.return_value.__iter__ = lambda self : iter(self.readline, '')
return f_open
私は次の解決策を見つけました:
text_file_data = '\n'.join(["a line here", "the second line", "another
line in the file"])
with patch('__builtin__.open', mock_open(read_data=text_file_data),
create=True) as m:
# mock_open doesn't properly handle iterating over the open file with for line in file:
# but if we set the return value like this, it works.
m.return_value.__iter__.return_value = text_file_data.splitlines()
with open('filename', 'rU') as f:
for line in f:
print line