I have successfully mocked a property with PropertyMock, but I can't figure out how to check which instance of the class has had that property called. How can I assert that the property was called on one object, but not on another?
Here's an example where I want to assert that foo1.is_big was called, and foo2.is_big was not:
from mock import PropertyMock, patch
class Foo(object):
def __init__(self, size):
self.size = size
#property
def is_big(self):
return self.size > 5
f = Foo(3)
g = Foo(10)
assert not f.is_big
assert g.is_big
with patch('__main__.Foo.is_big', new_callable=PropertyMock) as mock_is_big:
mock_is_big.return_value = True
foo1 = Foo(4)
foo2 = Foo(9)
should_pass = False
if should_pass:
is_big = foo1.is_big
else:
is_big = foo2.is_big
assert is_big
# How can I make this pass when should_pass is True, and fail otherwise?
mock_is_big.assert_called_once_with()
print('Done.')
The current code will pass when either one of them is called.
Maybe there's a better way, but I got it to work by creating a subclass of PropertyMock that records the instance that it was called on as one of the parameters to the mock call.
from mock import PropertyMock, patch
class Foo(object):
def __init__(self, size):
self.size = size
#property
def is_big(self):
return self.size > 5
class PropertyInstanceMock(PropertyMock):
"""Like PropertyMock, but records the instance that was called."""
def __get__(self, obj, obj_type):
return self(obj)
def __set__(self, obj, val):
self(obj, val)
with patch("__main__.Foo.is_big", new_callable=PropertyInstanceMock) as mock_is_big:
mock_is_big.return_value = True
foo1 = Foo(4)
foo2 = Foo(9)
should_pass = False
if should_pass:
is_big = foo1.is_big
else:
is_big = foo2.is_big
assert is_big
# Now this passes when should_pass is True, and fails otherwise.
mock_is_big.assert_called_once_with(foo1)
print("Done.")
Related
Better to provide an example i guess (a littler bit pseudo-codish...)
from django.db import transaction
from somewhere import some_job
from functools import partial
class Foo:
def do_something(self, key, value):
return some_job(key, value)
#property
def modifier(self):
pass
f = Foo()
f.do_something(key='a', value=1) -> result
f.modifier.do_something(key='a', value=1) -> transaction.on_commit(partial(do_something, key='a', value=1))
Normally if do_something is called it would do it regular thing and return some result,
but when it is chained via modifier it should return transaction.on_commit(partial(do_something, key='a', value=1)) instead of regular result. Modifier might be property or something else inside class. Problem is that this insinstance is a singletone and should not be changed permanently as it will be used latelly by other code.
Can not wrap my head around how to do this.
Any ideas?
As pointed out in the comments, you can have the modifier property return a quick-and-dirty wrapper class that implements the method do_something itself but does something different to the underlying foo instance.
class Foo:
def do_something(self, key, value):
print(f"Called unmodified on {self} :)")
#property
def modifier(self):
return ModifiedFoo(self)
class ModifiedFoo:
def __init__(self, foo):
self.foo = foo
def do_something(self, key, value):
print(f"Called modified on {self.foo} :)")
f = Foo()
f.do_something(key='a', value=1)
f.modifier.do_something(key='a', value=1)
class Modifier(Foo):
def do_something(this, key, value):
transaction.on_commit(partial(super().do_something, key='a', value=1))
This does modify the foo object, but it should work if you change the name for _lock so it doesn't collide with other attributes.
from functools import partial
class Foo:
_lock = False
def do_something(self, key, value):
if self._lock:
transaction.on_commit(partial(some_job, key, value))
self._lock = False
else:
return some_job(key, value)
def lock(self):
self._lock = True
return self
# some mocks
class Transaction:
#staticmethod
def on_commit(func):
print("Transaction commit success.")
func()
some_job = lambda x, y: print("Some job", x, y)
transaction = Transaction()
# end mocks
foo = Foo()
print(">>> foo.do_something(...)")
foo.do_something('key1', 'value1')
# Some job key1 value1
print(">>> foo.lock().do_something(...)")
foo.lock().do_something('key2', 'value2')
# Transaction commit success.
# Some job key2 value2
target code:
test_box = TestBox(Checkers.begin_with("op") and Checkers.end_with("or"))
test_box.run("operator") # True
test_box.run("option") # False
What I think(may be in the wrong way): TestBox may be a class or a method, and begin_with and end_with are classmethod of class Checkers, but how to make Checkers.begin_with("op") and Checkers.end_with("or") evaluated until test_box.run() was called?
I have found a solution which use lambda, post here:
class TestBox:
def __init__(self, func):
self._func = func
def run(self, string):
Checkers.string = string
return self._func()
class Checkers:
string = None
#classmethod
def begin_with(cls, val):
return cls.string.startswith(val)
#classmethod
def end_with(cls, val):
return cls.string.endswith(val)
if __name__ == '__main__':
test_box = TestBox(lambda: Checkers.begin_with("op") or Checkers.end_with("or"))
print(test_box.run("operator")) # True
print(test_box.run("xxtion")) # False
print(test_box.run("xxtionor")) # True
I want to make sure that I understood correctly how decorator as class works.
Let's say i have a decorator as a function that add an attribute to an object
def addx(obj):
obj.x = 10
return obj
#addx
class A:
pass
assert A.x == 10
Is it possible to write the same decorator as a class decorator? since the class decorator can't return the object itself with __init__
class addx:
def __init__(self, obj):
obj.x = 10
# ???
You could write an equivalent class-based decorator like this...
class addx:
def __new__(self, obj):
obj.x = 10
return obj
#addx
class A:
pass
assert A.x == 10
...but I don't think this really gets you anything. The utility of a class-based decorator becomes more apparent when your goal is to modify objects of class A, rather than class A itself. Compare the following two decorators, one function based and one class based:
def addx_func(kls):
def wrapper():
res = kls()
res.x = 10
return res
return wrapper
class addx_class:
def __init__(self, kls):
self.kls = kls
def __call__(self):
res = self.kls()
res.x = 10
return res
#addx_func
class A:
pass
#addx_class
class B:
pass
a = A()
assert a.x == 10
b = B()
assert b.x == 10
This is my code, my intention is to pass the method name as a parameter when I initialize the object and I want to run the method 'num' (second argument) of times. Basically get n number of results (as mentioned in 2nd argument).
class Foo(object):
faker = Faker()
def __init__(self, custom_method, num=1):
self.values = []
self.custom_method = custom_method
self.num = num
for x in self.num:
self.custom_method = self.values.append(custom_method)
def random_first_name(self):
self.custom_method = self.faker.first.name()
return self.custom_method
def random_phone(self):
self.custom_method = self.faker.random.phone()
return self.custom_method
b = Foo(random_first_name, 1)
c = Foo(random_phone,2)
I guess that you may want to use the function getattr.
class Foo(object):
faker = Faker()
def __init__(self, custom_method, num=1):
self.custom_method = custom_method
self.num = num
#property # Briefly, the property decorator makes the job of calling the callable for you. I.e. There is no need to do self.method(), self.method is enough.
def random_first_name(self):
return self.faker.first.name()
#property
def random_phone(self):
return self.faker.random.phone()
def call_method_num_times(self):
return [getattr(self, self.custom_method)\
for _ in range(self.num)]
I cannot instantiate this class, but this could be used as follows:
>>> foo1 = Foo('random_first_name', 1)
>>> foo1.call_method_num_times()
['John']
>>> foo2 = Foo('random_phone', 2)
>>> foo2.call_method_num_times()
['0123456789', '9876543210']
To (even more) reorganize your class in a (subjectively) better fashion, I would do
class Foo(object):
def __init__(self):
self.faker = Faker()
#property
def random_first_name(self):
return self.faker.first.name()
#property
def random_phone(self):
return self.faker.random.phone()
def call_method_num_times(self, custom_method, num=1):
return [getattr(self, custom_method)\
for _ in range(num)]
Thus allowing you for instantiating Foo only once
>>> foo = Foo()
>>> foo.call_method_num_times('random_first_name')
['John']
>>> foo.call_method_num_times('random_phone', 2)
['0123456789', '9876543210']
If you are not comfortable with the use of the python native property descriptor, you can keep your two methods as explicite ones. In this case, you would define the class Foo as follows
class Foo(object):
def __init__(self):
self.faker = Faker()
def random_first_name(self):
return self.faker.first.name()
def random_phone(self):
return self.faker.random.phone()
def call_method_num_times(self, custom_method, num=1):
return [getattr(self, custom_method)()\
for _ in range(num)]
Which would change nothing in ways of using Foo
>>> foo = Foo()
>>> foo.call_method_num_times('random_first_name')
['John']
>>> foo.call_method_num_times('random_phone', 2)
['0123456789', '9876543210']
Is it possible to make a property assert when it is changed (for the purpose of debugging)?
class MyClass(object):
def set_my_property(self, value):
self.my_property = value
# TODO: mark my_property so that if it gets set again, an assert
# is triggered
c = MyClass()
c.set_my_property(4)
# any of these lines would cause an assertion
c.set_my_property(8)
c.my_property = 123
EDIT:
Is this what you're looking for?
class MyClass(object):
def __init__(self):
self.trigger = False
self._my_property = 0
def set_my_property(self, value):
if self.trigger:
raise Exception("WHOOPS!")
self._my_property = value
# TODO: mark my_property so that if it gets set again, an assert
# is triggered
self.trigger = True
def get_my_property(self):
return self._my_property
my_property = property(get_my_property, set_my_property, None)
c = MyClass()
c.set_my_property(4)
# any of these lines would cause an assertion
c.set_my_property(8)
c.my_property = 123
Add a boolean to check if the value has been set before:
EDIT: But you want a property, so you'll need to create one:
class MyClass(object):
def __init__(self):
self.my_property_set = False
self._my_property = None
def set_my_property(self, value):
self._my_property = value
assert not self.my_property_set,"my_property already set"
self.my_property_set = True
def get_my_property(self):
return self._my_property
my_property = property(get_my_property, set_my_property, None)
c = MyClass()
c.set_my_property(4)
# any of these lines would cause an assertion
c.set_my_property(8)
c.my_property = 123
class Foo:
def __init__(self):
self._bar = None
#property
def bar(self): return self._bar
#bar.setter:
def bar(self, value):
assert value != some_constant # your assert condition
self._bar = value
#bar.deleter:
def bar(self, value):
assert value != some_constant # your assert condition
self._bar = None