Search code examples
djangofactory-boy

Django - Factoryboy: __str__ call with stringformating returns object instead


I'm creating a number of tests for a Django app whilst using factory-boy to generate the model instances. However, I have tests thats failing but I'm not entirely sure why this is the case.

    def test__str__(self) -> None:
        self.assertEqual(
            str(self.academy_settings),
            "Academy settings for {}".format(self.academy_settings.academy),
        )

As you can see I'm verifying a __str__ method on one of our django models, but when I run this test I get the following error.

Failure
Expected :"<AcademySettingsFactory for <class 'core.models.AcademySettings'>>"
Actual   :'Academy settings for <factory.declarations.SubFactory object at 0x1105ce438>'

These errors are somewhat puzzling to me. Its clear that the actual result is nothing like the result that I am expecting, but the same could be set for the expected result.

As you can see in the model definitions below both of them should return simple string objects.

class Academy(models.Model):
    name = models.CharField(
        max_length=400, unique=True
    )

    def __str__(self) -> str:
        return "{}".format(self.name)


class AcademySettings(models.Model):

    academy = models.ForeignKey(Academy, on_delete=models.CASCADE)
    (...)

    def __str__(self) -> str:
        return "Academy settings for {}".format(self.academy)

Now, as you can see in the factories below I've written some custom lazy_attribute to keep the name field unique, but even without that code the errors keeps getting raised. So I'm not sure what is the actual cause here - does anyone know what I am doing wrong?

class AcademyFactory(factory.DjangoModelFactory):
    class Meta:
        model = 'core.Academy'

    @factory.lazy_attribute
    def name(self):
        """
        The faker module has an habit of generating too many duplicate names
        every now and then. We solve this by appending the name with the
         instance.id
        """
        return '{} {}'.format(
            factory.Faker('company').generate(),
            str(factory.Sequence(lambda n: ' {}'.format(n))._creation_counter),
        )

class AcademySettingsFactory(factory.DjangoModelFactory):
    class Meta:
        model = 'core.AcademySettings'

    academy = factory.SubFactory(AcademyFactory)



Solution

  • In all likelihood, you're defining self.academy_settings = AcademySettingsFactory, thus referencing the class instead of getting an instance.

    This can be fixed with the following:

    def test__str__(self):
        academy_settings = AcademySettingsFactory()
        self.assertEqual(
            "Academy settings for {}".format(academy_settings.academy),
            str(academy_settings),
        )
    

    (Just a quick note: the call is self.assertEqual(expected, computed): put the expected value first, and the computed one last).