class 方法中使用的模拟 open() 函数
Mock open() function used in a class method
我试图模拟我 class 的方法中使用的 open 函数。
我找到了这个帖子 How do I mock an open used in a with statement (using the Mock framework in Python)? but could not solve my issue. Also the unittest documention shows a solution which also didn't mock my open https://docs.python.org/3/library/unittest.mock-examples.html#patch-decorators
这是我的class使用open函数的方法:
#__init.py__
import json
class MyClass:
def save_data_to_file(self, data):
with open('/tmp/data.json', 'w') as file:
json.dump(data, file)
...
mc = MyClass()
现在我找到了一些不同的解决方案。这是我的测试:
#save_to_file_test.py
from mymodule import MyClass
from mock import mock_open, patch
import ast
class SaveToFileTest(unittest.TestCase):
def setUp(self):
self.mc = MyClass()
self.data = [
{'id': 5414470, 'name': 'peter'},
{'id': 5414472, 'name': 'tom'},
{'id': 5414232, 'name': 'pit'},
]
def test_save_data_to_file(self):
m = mock_open()
with patch('mymodule.open', m, create=True):
self.mc.save_data_to_file(self.data)
string = ''
for call in m.return_value.write.mock_calls:
string += (call[1][0])
list = ast.literal_eval(string)
assertEquals = (list, self.data)
我不确定这是否是测试应写入文件的内容的最佳方式。
当我测试 mock_calls(call_args_list 相同)时,这是传递给文件句柄的参数。
欢迎任何意见、改进和建议。
TL;DR
您的问题的核心是您应该 还模拟 json.dump
以便能够正确测试将要写入您的文件的数据。在对您的测试方法进行一些重要调整之前,我实际上很难 运行 您的代码。
- 模拟
builtins.open
而不是 mymmodule.open
- 您在上下文管理器中,因此您应该检查
m.return_value.__enter__.write
,但您实际上是从 json.dump 调用写入,而写入将被调用。 (下面是建议解决方案的详细信息)
- 你还应该模拟
json.dump
来简单地验证它是用你的数据调用的
总之,有了上面提到的问题,方法可以重写为:
下面是关于所有这些的详细信息
def test_save_data_to_file(self):
with patch('builtins.open', new_callable=mock_open()) as m:
with patch('json.dump') as m_json:
self.mc.save_data_to_file(self.data)
# simple assertion that your open was called
m.assert_called_with('/tmp/data.json', 'w')
# assert that you called m_json with your data
m_json.assert_called_with(self.data, m.return_value)
详细说明
为了关注我在你的代码中看到的问题,我强烈建议做的第一件事,因为 open
是一个内置的,是从内置的模拟,此外,你可以节省一行代码通过使用 new_callable
和 as
,所以你可以简单地这样做:
with patch('builtins.open', new_callable=mock_open()) as m:
我在你的代码中看到的下一个问题,因为我遇到了麻烦 运行 在你开始循环你的调用时我实际上做了以下调整:
m.return_value.__enter__.return_value.write.mock_calls
要剖析这一点,您必须牢记的是您的方法正在使用上下文管理器。在使用上下文管理器时,您的写入工作实际上将在您的 __enter__
方法中完成。所以,从你的 m
的 return_value
,你想得到 __enter__
的 return_value。
但是,这将我们带入了您要测试的问题的核心。由于 json.dump
在写入文件时的工作方式,您在检查代码后写入的 mock_calls
实际上看起来像这样:
<MagicMock name='open().write' id='4348414496'>
call('[')
call('{')
call('"name"')
call(': ')
call('"peter"')
call(', ')
call('"id"')
call(': ')
call('5414470')
call('}')
call(', ')
call('{')
call('"name"')
call(': ')
call('"tom"')
call(', ')
call('"id"')
call(': ')
call('5414472')
call('}')
call(', ')
call('{')
call('"name"')
call(': ')
call('"pit"')
call(', ')
call('"id"')
call(': ')
call('5414232')
call('}')
call(']')
call.__str__()
测试不会很有趣。因此,这将我们带到您可以尝试的下一个解决方案;模拟 json.dump
.
您不应该测试 json.dump,您应该测试使用正确的参数调用它。话虽如此,您可以在模拟中遵循类似的方式并执行以下操作:
with patch('json.dump') as m_json:
现在,有了它,您可以显着简化您的测试代码,以简单地验证是否使用您正在测试的数据调用该方法。所以,有了这个,当你把它们放在一起时,你会得到这样的东西:
def test_save_data_to_file(self):
with patch('builtins.open', new_callable=mock_open()) as m:
with patch('json.dump') as m_json:
self.mc.save_data_to_file(self.data)
# simple assertion that your open was called
m.assert_called_with('/tmp/data.json', 'w')
# assert that you called m_json with your data
m_json.assert_called_with(self.data, m.return_value.__enter__.return_value)
如果您有兴趣进一步重构以使您的测试方法更简洁一些,您还可以将您的补丁设置为装饰器,让您的代码更简洁地留在方法中:
@patch('json.dump')
@patch('builtins.open', new_callable=mock_open())
def test_save_data_to_file(self, m, m_json):
self.mc.save_data_to_file(self.data)
# simple assertion that your open was called
m.assert_called_with('/tmp/data.json', 'w')
# assert that you called m_json with your data
m_json.assert_called_with(self.data, m.return_value.__enter__.return_value)
检查是您最好的朋友,查看在哪些步骤调用了哪些方法,以进一步帮助测试。祝你好运。
我试图模拟我 class 的方法中使用的 open 函数。 我找到了这个帖子 How do I mock an open used in a with statement (using the Mock framework in Python)? but could not solve my issue. Also the unittest documention shows a solution which also didn't mock my open https://docs.python.org/3/library/unittest.mock-examples.html#patch-decorators
这是我的class使用open函数的方法:
#__init.py__
import json
class MyClass:
def save_data_to_file(self, data):
with open('/tmp/data.json', 'w') as file:
json.dump(data, file)
...
mc = MyClass()
现在我找到了一些不同的解决方案。这是我的测试:
#save_to_file_test.py
from mymodule import MyClass
from mock import mock_open, patch
import ast
class SaveToFileTest(unittest.TestCase):
def setUp(self):
self.mc = MyClass()
self.data = [
{'id': 5414470, 'name': 'peter'},
{'id': 5414472, 'name': 'tom'},
{'id': 5414232, 'name': 'pit'},
]
def test_save_data_to_file(self):
m = mock_open()
with patch('mymodule.open', m, create=True):
self.mc.save_data_to_file(self.data)
string = ''
for call in m.return_value.write.mock_calls:
string += (call[1][0])
list = ast.literal_eval(string)
assertEquals = (list, self.data)
我不确定这是否是测试应写入文件的内容的最佳方式。 当我测试 mock_calls(call_args_list 相同)时,这是传递给文件句柄的参数。 欢迎任何意见、改进和建议。
TL;DR
您的问题的核心是您应该 还模拟 json.dump
以便能够正确测试将要写入您的文件的数据。在对您的测试方法进行一些重要调整之前,我实际上很难 运行 您的代码。
- 模拟
builtins.open
而不是mymmodule.open
- 您在上下文管理器中,因此您应该检查
m.return_value.__enter__.write
,但您实际上是从 json.dump 调用写入,而写入将被调用。 (下面是建议解决方案的详细信息) - 你还应该模拟
json.dump
来简单地验证它是用你的数据调用的
总之,有了上面提到的问题,方法可以重写为:
下面是关于所有这些的详细信息
def test_save_data_to_file(self):
with patch('builtins.open', new_callable=mock_open()) as m:
with patch('json.dump') as m_json:
self.mc.save_data_to_file(self.data)
# simple assertion that your open was called
m.assert_called_with('/tmp/data.json', 'w')
# assert that you called m_json with your data
m_json.assert_called_with(self.data, m.return_value)
详细说明
为了关注我在你的代码中看到的问题,我强烈建议做的第一件事,因为 open
是一个内置的,是从内置的模拟,此外,你可以节省一行代码通过使用 new_callable
和 as
,所以你可以简单地这样做:
with patch('builtins.open', new_callable=mock_open()) as m:
我在你的代码中看到的下一个问题,因为我遇到了麻烦 运行 在你开始循环你的调用时我实际上做了以下调整:
m.return_value.__enter__.return_value.write.mock_calls
要剖析这一点,您必须牢记的是您的方法正在使用上下文管理器。在使用上下文管理器时,您的写入工作实际上将在您的 __enter__
方法中完成。所以,从你的 m
的 return_value
,你想得到 __enter__
的 return_value。
但是,这将我们带入了您要测试的问题的核心。由于 json.dump
在写入文件时的工作方式,您在检查代码后写入的 mock_calls
实际上看起来像这样:
<MagicMock name='open().write' id='4348414496'>
call('[')
call('{')
call('"name"')
call(': ')
call('"peter"')
call(', ')
call('"id"')
call(': ')
call('5414470')
call('}')
call(', ')
call('{')
call('"name"')
call(': ')
call('"tom"')
call(', ')
call('"id"')
call(': ')
call('5414472')
call('}')
call(', ')
call('{')
call('"name"')
call(': ')
call('"pit"')
call(', ')
call('"id"')
call(': ')
call('5414232')
call('}')
call(']')
call.__str__()
测试不会很有趣。因此,这将我们带到您可以尝试的下一个解决方案;模拟 json.dump
.
您不应该测试 json.dump,您应该测试使用正确的参数调用它。话虽如此,您可以在模拟中遵循类似的方式并执行以下操作:
with patch('json.dump') as m_json:
现在,有了它,您可以显着简化您的测试代码,以简单地验证是否使用您正在测试的数据调用该方法。所以,有了这个,当你把它们放在一起时,你会得到这样的东西:
def test_save_data_to_file(self):
with patch('builtins.open', new_callable=mock_open()) as m:
with patch('json.dump') as m_json:
self.mc.save_data_to_file(self.data)
# simple assertion that your open was called
m.assert_called_with('/tmp/data.json', 'w')
# assert that you called m_json with your data
m_json.assert_called_with(self.data, m.return_value.__enter__.return_value)
如果您有兴趣进一步重构以使您的测试方法更简洁一些,您还可以将您的补丁设置为装饰器,让您的代码更简洁地留在方法中:
@patch('json.dump')
@patch('builtins.open', new_callable=mock_open())
def test_save_data_to_file(self, m, m_json):
self.mc.save_data_to_file(self.data)
# simple assertion that your open was called
m.assert_called_with('/tmp/data.json', 'w')
# assert that you called m_json with your data
m_json.assert_called_with(self.data, m.return_value.__enter__.return_value)
检查是您最好的朋友,查看在哪些步骤调用了哪些方法,以进一步帮助测试。祝你好运。