1

I have just encountered a bug with my unit test, because I was essentially monkey patching in the set up like so:

def test_some_class_to_string():
    some_class = SomeClass()
    some_class.foo = 'bar'
    some_class.monkey_patched = 'baz'
    assert str(some_class) == 'barbaz'

class SomeClass(models.Model):
    foo = models.CharField(max_length=100)

    def __str__(self):
        #monkey_patched property removed from model
        return '{0}{1}'.format(self.foo, self.monkey_patched)

I had removed a property from SomeClass but the str method was still passing in the unit test due to re-adding monkey_patched at runtime.

Is it best to deal with this particular situation by calling str(some_class) before any property setup as an additional test, or should I always use keyword arguments to initialise classes in unit tests?

Tiger_Mike
  • 136
  • 8
  • Have you considered redesigning the class to avoid the need for such monkey-patching to test it? – chepner Oct 31 '17 at 18:33

2 Answers2

1

You can try an assertion before the assignement:

def test_some_class_to_string():
    some_class = SomeClass()
    assert some_class.foo and some_class.bar
    some_class.foo = 'bar'
    some_class.monkey_patched = 'baz'
    assert str(some_class) == 'barbaz'
doze
  • 287
  • 2
  • 13
1

Use a library like mock to assist you with doing this. Say you have a class defined like this:

class A:
    def __init__(self, x):
        self.x = x
        self.y = 6

Now, instead of simply creating an instance of A and doing whatever you want with it, create a mock object that is an instrumented version of an instance of A:

>>> m = unittest.mock.Mock(spec_set=A)
>>> m.z = 9
Traceback (most recent call last):
  File "tmp.py", line 11, in <module>
    m.z = 9
  File ".../unittest/mock.py", line 684, in __setattr__
    raise AttributeError("Mock object has no attribute '%s'" % name)
AttributeError: Mock object has no attribute 'z'
chepner
  • 497,756
  • 71
  • 530
  • 681