Sh4kE
Sh4kE

Reputation: 85

factoryboy not overriding django model properties

I'm trying to override a custom Django model property via factory_boy for testing purposes. But it seems like it is simply taking the default behavior of the model. is factory boy not able to change the default behaviour of custom attributes?

Here is a basic test I wrote:

models.py:

class Session(models.Model):
    name = models.CharField(max_length=100)

    @property
    def foo(self):
        return method_not_callable_in_testing()

def method_not_callable_in_testing():
    return 42

SessionFactory.py:

class SessionFactory(factory.django.DjangoModelFactory):
    class Meta:
        model = Session

    name = "session"
    foo = 1337

tests.py: class TestSession(TestCase):

def test_custom_attribute_overwritten_by_factoryboy(self):
    session = SessionFactory.create()
    self.assertEquals(session.foo, 1337)

When running the tests I get the following error:

F
======================================================================
FAIL: test_custom_attribute_overwritten_by_factoryboy (bar.tests.TestSession)
----------------------------------------------------------------------
Traceback (most recent call last):
  File "/home/sh4ke/repos/foo/bar/tests.py", line 10, in test_custom_attribute_overwritten_by_factoryboy
    self.assertEquals(session.foo, 1337)
AssertionError: 42 != 1337

Upvotes: 4

Views: 2211

Answers (1)

daphtdazz
daphtdazz

Reputation: 8159

tl;dr

factory_boy can change "custom attributes", (which I assume you mean ones that are not django Fields), but it can't change read-only attributes, which is what you are trying to do.

More info

Your problem is that you are trying to use the factory to set an attribute for a read-only property. Roughly, the combination of factory_boy and django means that the call to SessionFactory() is doing:

session = Session()
session.foo = 1337

But I wouldn't expect this to work because unless you declare the @foo.setter method @property assumes you wanted a read-only property, and so raises attribute error when you attempt to set it.

The weird bit is why you don't see that error, and it turns out that django quietly suppresses it! It sees that your model has the foo attribute by doing getattr(session, 'foo'), but it has wrapped the whole thing including the setattr inside a try: except AttributeError:, so while it detects and raises an attempt to set an attribute that doesn't exist at all, it also catches and quietly ignores the failed case of an attempt to set a read-only property.

I suspect this is a bug... so will probably try and raise it with the django community.

Upvotes: 1

Related Questions