如何使用相同的语法模拟 pathlib.Path.open 和 pathlib.path.unlink?
How can I mock pathlib.Path.open and pathlib.path.unlink with the same syntax?
我在生产代码中使用 pathlib.Path.open()
和 pathlib.Path.unlink()
。该作品的 unittest
。但是我用两种不同的方式来patch()
。一个带有 @patch
装饰器,一个带有上下文管理器 with mock.patch()
.
我只想@patch
这样。
class MyTest(unittest.TestCase):
@mock.patch('pathlib.Path.unlink')
@mock.patch('pathlib.Path.open')
def test_foobar(self, mock_open, mock_unlink):
但是当前的真实代码是这样的
import unittest
from unittest import mock
import pathlib
class MyTest(unittest.TestCase):
@mock.patch('pathlib.Path.unlink')
def test_foobar(self, mock_unlink):
# simulated CSV file
opener = mock.mock_open(read_data='A;B\n1;2')
with mock.patch('pathlib.Path.open', opener):
result = validate_csv(file_path=pathlib.Path('foo.csv'))
self.assertTrue(result)
我的技术问题是,在使用 @patch
装饰器时,我不知道如何将我的 CSV 内容添加到 mock_open
。
它可能看起来像这样:
class MyTest(unittest.TestCase):
@mock.patch('pathlip.Path.open')
@mock.patch('pathlib.Path.unlink')
def test_foobar(self, mymock_unlink, mymock_open):
# simulated CSV file
opener = mock.mock_open(read_data='A;B\n1;2')
# QUESTION: How do I bring 'opener' and 'mymock_open'
# together now?
result = validate_csv(file_path=pathlib.Path('foo.csv'))
self.assertTrue(result)
但是我的问题的目的是提高代码的可读性和可维护性。使用两个装饰器可以减少缩进。选择一种方式(装饰器或上下文管理器)恕我直言,更容易阅读。
出于学习目的
问:现在如何将 'opener' 和 'mymock_open' 放在一起?
A: 将 mymock_open
的 side_effect
和 return_value
分配给 opener
的 side_effect
和 return_value
。
@mock.patch('pathlib.Path.open')
@mock.patch('pathlib.Path.unlink')
def test_foobar(self, mymock_unlink, mymock_open):
# simulated CSV file
opener = mock.mock_open(read_data='A;B\n1;2')
# QUESTION: How do I bring 'opener' and 'mymock_open'
# together now?
mymock_open.side_effect = opener.side_effect # +
mymock_open.return_value = opener.return_value # +
result = validate_csv(file_path=pathlib.Path('foo.csv'))
opener.assert_not_called() # +
mymock_open.assert_called_once() # +
mymock_unlink.assert_called_once() # +
self.assertTrue(result)
但这算不上可读性的提高。
都使用装饰器
@mock.patch('pathlib.Path.open', new_callable=lambda: mock.mock_open(read_data='A;B\n1;2')) # +
@mock.patch('pathlib.Path.unlink')
def test_foobar(self, mock_unlink, mock_open):
result = validate_csv(file_path=pathlib.Path('foo.csv'))
mock_open.assert_called_once() # +
mock_unlink.assert_called_once() # +
self.assertTrue(result)
仅传递 mock.mock_open(read_data='A;B\n1;2')
(作为位置参数 new
)而不是 new_callable=lambda: ...
也可以,但是 @mock.patch
不会将 mock_open
传递给 test_foobar
.
两者都使用上下文管理器
def test_foobar(self):
# simulated CSV file
opener = mock.mock_open(read_data='A;B\n1;2')
with mock.patch('pathlib.Path.unlink') as mock_unlink,\
mock.patch('pathlib.Path.open', opener) as mock_open: # +
self.assertIs(mock_open, opener) # +
result = validate_csv(file_path=pathlib.Path('foo.csv'))
mock_open.assert_called_once() # +
mock_unlink.assert_called_once() # +
self.assertTrue(result)
请注意 mock_open
与 opener
是同一个实例。
验证解决方案
validate_csv
的最小可重现示例的实施示例:
def validate_csv(file_path):
"""
:param pathlib.Path file_path:
:rtype: bool
"""
with file_path.open() as f:
data = f.read()
file_path.unlink()
return data == 'A;B\n1;2'
我在生产代码中使用 pathlib.Path.open()
和 pathlib.Path.unlink()
。该作品的 unittest
。但是我用两种不同的方式来patch()
。一个带有 @patch
装饰器,一个带有上下文管理器 with mock.patch()
.
我只想@patch
这样。
class MyTest(unittest.TestCase):
@mock.patch('pathlib.Path.unlink')
@mock.patch('pathlib.Path.open')
def test_foobar(self, mock_open, mock_unlink):
但是当前的真实代码是这样的
import unittest
from unittest import mock
import pathlib
class MyTest(unittest.TestCase):
@mock.patch('pathlib.Path.unlink')
def test_foobar(self, mock_unlink):
# simulated CSV file
opener = mock.mock_open(read_data='A;B\n1;2')
with mock.patch('pathlib.Path.open', opener):
result = validate_csv(file_path=pathlib.Path('foo.csv'))
self.assertTrue(result)
我的技术问题是,在使用 @patch
装饰器时,我不知道如何将我的 CSV 内容添加到 mock_open
。
它可能看起来像这样:
class MyTest(unittest.TestCase):
@mock.patch('pathlip.Path.open')
@mock.patch('pathlib.Path.unlink')
def test_foobar(self, mymock_unlink, mymock_open):
# simulated CSV file
opener = mock.mock_open(read_data='A;B\n1;2')
# QUESTION: How do I bring 'opener' and 'mymock_open'
# together now?
result = validate_csv(file_path=pathlib.Path('foo.csv'))
self.assertTrue(result)
但是我的问题的目的是提高代码的可读性和可维护性。使用两个装饰器可以减少缩进。选择一种方式(装饰器或上下文管理器)恕我直言,更容易阅读。
出于学习目的
问:现在如何将 'opener' 和 'mymock_open' 放在一起?
A: 将 mymock_open
的 side_effect
和 return_value
分配给 opener
的 side_effect
和 return_value
。
@mock.patch('pathlib.Path.open')
@mock.patch('pathlib.Path.unlink')
def test_foobar(self, mymock_unlink, mymock_open):
# simulated CSV file
opener = mock.mock_open(read_data='A;B\n1;2')
# QUESTION: How do I bring 'opener' and 'mymock_open'
# together now?
mymock_open.side_effect = opener.side_effect # +
mymock_open.return_value = opener.return_value # +
result = validate_csv(file_path=pathlib.Path('foo.csv'))
opener.assert_not_called() # +
mymock_open.assert_called_once() # +
mymock_unlink.assert_called_once() # +
self.assertTrue(result)
但这算不上可读性的提高。
都使用装饰器
@mock.patch('pathlib.Path.open', new_callable=lambda: mock.mock_open(read_data='A;B\n1;2')) # +
@mock.patch('pathlib.Path.unlink')
def test_foobar(self, mock_unlink, mock_open):
result = validate_csv(file_path=pathlib.Path('foo.csv'))
mock_open.assert_called_once() # +
mock_unlink.assert_called_once() # +
self.assertTrue(result)
仅传递 mock.mock_open(read_data='A;B\n1;2')
(作为位置参数 new
)而不是 new_callable=lambda: ...
也可以,但是 @mock.patch
不会将 mock_open
传递给 test_foobar
.
两者都使用上下文管理器
def test_foobar(self):
# simulated CSV file
opener = mock.mock_open(read_data='A;B\n1;2')
with mock.patch('pathlib.Path.unlink') as mock_unlink,\
mock.patch('pathlib.Path.open', opener) as mock_open: # +
self.assertIs(mock_open, opener) # +
result = validate_csv(file_path=pathlib.Path('foo.csv'))
mock_open.assert_called_once() # +
mock_unlink.assert_called_once() # +
self.assertTrue(result)
请注意 mock_open
与 opener
是同一个实例。
验证解决方案
validate_csv
的最小可重现示例的实施示例:
def validate_csv(file_path):
"""
:param pathlib.Path file_path:
:rtype: bool
"""
with file_path.open() as f:
data = f.read()
file_path.unlink()
return data == 'A;B\n1;2'