Reputation: 4026
I want to test that an instance variable is "set" to specific values (i.e. multiple times) when a Python method is called.
Replacing the instance variable with a PropertyMock
allows me to view the mock_calls
and verify what values the property was set to. However, PropertyMock
doesn't behave like a normal variable. When you set a value on it and try to read it, you get back another Mock. Is there a way to receive the value back instead?
Here's a contrived example:
import time
from unittest.mock import PropertyMock, call
class Machine:
def __init__(self):
self.mode = "idle"
def start(self):
# Update mode
self.mode = "running"
# Do some work, e.g. drive a motor for 0.5 sec
time.sleep(0.5)
# Restore mode
if self.mode == "running": # Should always be True, but isn't when using PropertyMock
self.mode = "idle"
class Test_Machine:
def test(self):
# Create a machine
real_machine = Machine()
# Mock the 'mode' property
mocked_property = PropertyMock()
type(real_machine).mode = mocked_property
# Call the method to test
real_machine.start()
print(mocked_property.mock_calls) # [call('running'), call(), call().__eq__('running')]
assert call("running") == mocked_property.mock_calls[0] # Success
assert call("idle") == mocked_property.mock_calls[-1] # Fails here
Upvotes: 2
Views: 609
Reputation: 16855
I'm sure there is a better way to do this, but if you are just interested in the calls of the property setter, and want the getter to behave as the original property, you could override PropertyMock
to behave like that:
class MyPropertyMock(PropertyMock):
def __init__(self, value=None):
super().__init__()
self.value = value
def __get__(self, obj, obj_type):
return self.value # the mock will not register these calls
def __set__(self, obj, val):
self.value = val
super().__set__(obj, val) # ensure the mock behavior in the setter
class Test_Machine:
def test(self):
real_machine = Machine()
mocked_property = MyPropertyMock(real_machine.value)
Machine.mode = mocked_property
real_machine.start()
print(mocked_property.mock_calls) # [call('running'), call('idle')]
...
Upvotes: 1