I have a base class that defines a class attribute and some child classes that depend on it, e.g.
class Base(object):
assignment = dict(a=1, b=2, c=3)
I want to unittest this class with different assignments, e.g. empty dictionary, single item, etc. This is extremely simplified of course, it's not a matter of refactoring my classes or tests
The (pytest) tests I have come up with, eventually, that work are
from .base import Base
def test_empty(self):
with mock.patch("base.Base.assignment") as a:
a.__get__ = mock.Mock(return_value={})
assert len(Base().assignment.values()) == 0
def test_single(self):
with mock.patch("base.Base.assignment") as a:
a.__get__ = mock.Mock(return_value={'a':1})
assert len(Base().assignment.values()) == 1
This feels rather complicated and hacky - I don't even fully understand why it works (I am familiar with descriptors though). Does mock automagically transform class attributes into descriptors?
A solution that would feel more logical does not work:
def test_single(self):
with mock.patch("base.Base") as a:
a.assignment = mock.PropertyMock(return_value={'a':1})
assert len(Base().assignment.values()) == 1
or just
def test_single(self):
with mock.patch("base.Base") as a:
a.assignment = {'a':1}
assert len(Base().assignment.values()) == 1
Other variants that I've tried don't work either (assignments remains unchanged in the test).
What's the proper way to mock a class attribute? Is there a better / more understandable way than the one above?
Perhaps I'm missing something, but isn't this possible without using
PropertyMock
?Here is an example how to unit-test your
Base
class:dict
andint
)@patch
decorator andpytest
framework with withpython 2.7+
or3+
.base.Base.assignment
is simply replaced with aMock
object. You made it a descriptor by adding a__get__
method.It's a little verbose and a little unnecessary; you could simply set
base.Base.assignment
directly:This isn't too safe when using test concurrency, of course.
To use a
PropertyMock
, I'd use:or even:
If your class (Queue for example) in already imported inside your test - and you want to patch MAX_RETRY attr - you can use @patch.object or simply better @patch.multiple
To improve readability you can use the
@patch
decorator:You can find more details at http://www.voidspace.org.uk/python/mock/patch.html#mock.patch.