如何模拟文件 IO 以便我可以覆盖单元测试中的名称属性?
How do I mock a a file IO so that I can override the name attribute in a unit test?
我正在尝试编写一个单元测试模拟,打开一个文件并将其传递给一个函数,该函数使用它来将 JSON 对象转储到其中。如何创建模拟打开文件句柄行为但使用相似属性的假 IO 对象,特别是 .name
?
我在这里阅读了大量的答案,他们都以不同的方式解决了这个问题。我试过模拟补丁 builtins.open
,我试过模拟补丁 open
在我的模块中被调用,但是我保留 运行 的主要错误是当我尝试访问假 IO 对象的 .name
属性,我得到一个 AttributeError:
AttributeError: 'CallbackStream' object has no attribute 'name'
所以这是一个简单的函数,它以 JSON 格式将字典写入磁盘并获取打开的文件句柄:
def generate(data, json_file):
# data is a dict
logging.info(f"Writing out spec file to {json_file.name}")
json.dump(data, json_file)
这是我尝试进行单元测试的内容:
@patch("builtins.open", new_callable=mock_open())
def test_generate_json_returns_zero(self, mock_open):
mocked_file = mock_open()
mocked_file.name = "FakeFileName"
data = {'stuff': 'stuff2'}
generate(data, json_file=mocked_file)
但是,这会产生上面的 AttributeError,我不能在其中使用 json_file.name
,因为它不作为属性存在。我以为明确设置它会起作用,但它没有。
我可以通过`tempfile.TemporaryFile:
使用临时文件绕过这个问题
def test_generate_json_returns_zero(self, mock_open):
data = {'stuff': 'stuff2'}
t = TemporaryFile("w")
generate(data, json_file=t)
但这并没有解决 实际 问题,我不知道如何模拟文件句柄,这样我实际上就不需要创建磁盘上的真实文件。
我无法克服 .name
不是有效属性的问题。我如何模拟文件对象,以便我可以实际使用 IO 对象的 .name
属性并仍然伪造一个 json.dump()
给它?
new_callable
参数是 Mock
的替代 class,因此当您调用:
@patch("builtins.open", new_callable=mock_open())
它通过用 mock_open()
returns 代替 builtins.open
来修补 builtins.open
,而不是 MagicMock
对象,这是您实际需要的,所以将行更改为简单地:
@patch("builtins.open")
它应该可以工作。
您的测试从未实际调用 open
,因此无需修补它。只需创建一个具有您需要的属性的 Mock
实例。
def test_generate_json_returns_zero(self):
mocked_file = Mock()
mocked_file.name = "FakeFileName"
data = {'stuff': 'stuff2'}
generate(data, json_file=mocked_file)
我正在尝试编写一个单元测试模拟,打开一个文件并将其传递给一个函数,该函数使用它来将 JSON 对象转储到其中。如何创建模拟打开文件句柄行为但使用相似属性的假 IO 对象,特别是 .name
?
我在这里阅读了大量的答案,他们都以不同的方式解决了这个问题。我试过模拟补丁 builtins.open
,我试过模拟补丁 open
在我的模块中被调用,但是我保留 运行 的主要错误是当我尝试访问假 IO 对象的 .name
属性,我得到一个 AttributeError:
AttributeError: 'CallbackStream' object has no attribute 'name'
所以这是一个简单的函数,它以 JSON 格式将字典写入磁盘并获取打开的文件句柄:
def generate(data, json_file):
# data is a dict
logging.info(f"Writing out spec file to {json_file.name}")
json.dump(data, json_file)
这是我尝试进行单元测试的内容:
@patch("builtins.open", new_callable=mock_open())
def test_generate_json_returns_zero(self, mock_open):
mocked_file = mock_open()
mocked_file.name = "FakeFileName"
data = {'stuff': 'stuff2'}
generate(data, json_file=mocked_file)
但是,这会产生上面的 AttributeError,我不能在其中使用 json_file.name
,因为它不作为属性存在。我以为明确设置它会起作用,但它没有。
我可以通过`tempfile.TemporaryFile:
使用临时文件绕过这个问题 def test_generate_json_returns_zero(self, mock_open):
data = {'stuff': 'stuff2'}
t = TemporaryFile("w")
generate(data, json_file=t)
但这并没有解决 实际 问题,我不知道如何模拟文件句柄,这样我实际上就不需要创建磁盘上的真实文件。
我无法克服 .name
不是有效属性的问题。我如何模拟文件对象,以便我可以实际使用 IO 对象的 .name
属性并仍然伪造一个 json.dump()
给它?
new_callable
参数是 Mock
的替代 class,因此当您调用:
@patch("builtins.open", new_callable=mock_open())
它通过用 mock_open()
returns 代替 builtins.open
来修补 builtins.open
,而不是 MagicMock
对象,这是您实际需要的,所以将行更改为简单地:
@patch("builtins.open")
它应该可以工作。
您的测试从未实际调用 open
,因此无需修补它。只需创建一个具有您需要的属性的 Mock
实例。
def test_generate_json_returns_zero(self):
mocked_file = Mock()
mocked_file.name = "FakeFileName"
data = {'stuff': 'stuff2'}
generate(data, json_file=mocked_file)