[英]side_effect function of PropertyMock gets called only once
I have two questions regarding the mocking of a property with unittest.mock.PropertyMock (code will follow below):我有两个关于使用 unittest.mock.PropertyMock 模拟属性的问题(代码如下):
test_prop
output "text_0, text_0" and not "text_0, text_1" as the test test_func
?为什么测试test_prop
输出 "text_0, text_0" 而不是 "text_0, text_1" 作为测试test_func
? The output indicates, that the function side_effect_func
in test_prop
gets called only once, while I would have expected it to get called twice, like in test_func
.输出表明, test_prop 中的函数side_effect_func
test_prop
被调用一次,而我预计它会被调用两次,就像在test_func
中一样。 My use case is that I would like to have a mock that returns a different name (which is a property) depending on how often it was called.我的用例是我想要一个根据调用频率返回不同名称(这是一个属性)的模拟。 This would "simulate" two different instances of Class1 to Class2 in the following minimal example.这将在以下最小示例中“模拟” Class1 到 Class2 的两个不同实例。
The code:编码:
File dut.py :文件dut.py :
class Class1():
def __init__(self, name):
self.__name = name
@property
def name(self):
return self.__name
def name_func(self):
return self.__name
class Class2():
def __init__(self, name, class1):
self.__name = name
self.__class1 = class1
@property
def name(self):
return self.__name
@property
def class1(self):
return self.__class1
File test\test_dut.py (the second with-statement produces the exact same behavior when swapped with the first one):文件test\test_dut.py (第二个 with 语句与第一个交换时产生完全相同的行为):
import dut
import unittest
from unittest.mock import patch, PropertyMock
class TestClass2(unittest.TestCase):
def test_func(self):
side_effect_counter = -1
def side_effect_func(_):
nonlocal side_effect_counter
side_effect_counter += 1
return f'text_{side_effect_counter}'
c2_1 = dut.Class2('class2', dut.Class1('class1'))
c2_2 = dut.Class2('class2_2', dut.Class1('class1_2'))
with patch('test_dut.dut.Class1.name_func', side_effect=side_effect_func, autospec=True):
print(f'{c2_2.class1.name_func()}, {c2_1.class1.name_func()}')
def test_prop(self):
side_effect_counter = -1
def side_effect_func():
nonlocal side_effect_counter
side_effect_counter += 1
return f'text_{side_effect_counter}'
c2_1 = dut.Class2('class2', dut.Class1('class1'))
c2_2 = dut.Class2('class2_2', dut.Class1('class1_2'))
with patch.object(dut.Class1, 'name', new_callable=PropertyMock(side_effect=side_effect_func)):
# with patch('test_dut.dut.Class1.name', new_callable=PropertyMock(side_effect=side_effect_func)):
print(f'{c2_2.class1.name}, {c2_1.class1.name}')
Call from command line: pytest -rP test\test_dut.py
This leads to the following output (problematic line marked by me):从命令行调用: pytest -rP test\test_dut.py
这将导致以下输出(我标记的问题行):
============================================================================================== test session starts ==============================================================================================
platform win32 -- Python 3.9.12, pytest-7.1.2, pluggy-1.0.0
rootdir: C:\Users\klosemic\Documents\playground_mocks
plugins: hypothesis-6.46.5, cov-3.0.0, forked-1.4.0, html-3.1.1, metadata-2.0.1, xdist-2.5.0
collected 2 items
test\test_dut.py .. [100%]
==================================================================================================== PASSES =====================================================================================================
_____________________________________________________________________________________________ TestClass2.test_func ______________________________________________________________________________________________
--------------------------------------------------------------------------------------------- Captured stdout call ----------------------------------------------------------------------------------------------
text_0, text_1
_____________________________________________________________________________________________ TestClass2.test_prop ______________________________________________________________________________________________
--------------------------------------------------------------------------------------------- Captured stdout call ----------------------------------------------------------------------------------------------
text_0, text_0 <<<<<< HERE IS THE PROBLEM
=============================================================================================== 2 passed in 0.46s ===============================================================================================
The issue has to do with how you are instantiating the PropertyMock
.问题与您如何实例化PropertyMock
有关。 To answer your first question about why the second test prints test_0
for both calls, you instantiate the PropertyMock
class during your with patch.object(get_events.Class1, 'name', new_callable=PropertyMock(side_effect=side_effect_func))
call.要回答关于为什么第二个测试为两个调用都打印test_0
的第一个问题,您在with patch.object(get_events.Class1, 'name', new_callable=PropertyMock(side_effect=side_effect_func))
调用期间实例化PropertyMock
类。
Since you instantiate the class, it gets called immediately at the end of that line due to the logic of the __enter__
method in the patch
object.由于您实例化了该类,因此由于patch
对象中__enter__
方法的逻辑,它会在该行的末尾立即被调用。 You can see that logic in this line and then this one .您可以在这一行中看到该逻辑,然后在这一行中看到该逻辑。 Due to that the value of your side_effect
immediately becomes a string, which is essentially the output of the first call to the PropertyMock
.由于你的side_effect
的值立即变成一个字符串,它本质上是第一次调用PropertyMock
的输出。 This can be confirmed by changing your function to the following and observing the output:这可以通过将您的函数更改为以下内容并观察输出来确认:
with patch.object(dut.Class1, 'name', new_callable=PropertyMock(side_effect=side_effect_func)) as mock_prop:
# print(f'{c2_2.class1.name}, {c2_1.class1.name}')
print(mock_prop)
You will notice that this prints text_0
in the console, confirming what has been mentioned above.您会注意到这会在控制台中打印text_0
,从而确认上面提到的内容。
To answer your second question, the way to use PropertyMock
in this case would be to change the second test to the following:要回答您的第二个问题,在这种情况下使用PropertyMock
的方法是将第二个测试更改为以下内容:
with patch.object(dut.Class1, 'name', new_callable=PropertyMock) as mock_prop:
mock_prop.side_effect = side_effect_func
print(f'{c2_2.class1.name}, {c2_1.class1.name}')
Then when you run the tests you get the correct output as shown below.然后,当您运行测试时,您会得到正确的输出,如下所示。
============================================================= test session starts =============================================================
platform darwin -- Python 3.8.9, pytest-7.0.1, pluggy-1.0.0
rootdir: ***
plugins: asyncio-0.18.3, mock-3.7.0
asyncio: mode=strict
collected 2 items
tests/test_dut.py .. [100%]
=================================================================== PASSES ====================================================================
____________________________________________________________ TestClass2.test_func _____________________________________________________________
------------------------------------------------------------ Captured stdout call -------------------------------------------------------------
text_0, text_1
____________________________________________________________ TestClass2.test_prop _____________________________________________________________
------------------------------------------------------------ Captured stdout call -------------------------------------------------------------
text_0, text_1
============================================================== 2 passed in 0.01s ==============================================================
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.