Search code examples
pythondjangofactory-boy

Django / FactoryBoy - Equal queries not equal?


I am writing tests for a Django application and one of my tests is failing with a strange bug where a assertEqual comparison is failing, even though the objects in both querysets match.

The test is quite big, so I've written a small test to recreate the error:

class StrangeBehaviorTest(TestCase):

    def test_init(self):
        purchase = ArrangementPurchaseFactory()

        self.assertTrue(purchase)
        self.assertTrue(purchase.arrangement_period)
        self.assertEqual(ArrangementPurchase.objects.count(), 1)

        fetched = ArrangementPurchase.objects.filter(
            id=1)

        self.assertEqual(fetched.first().id, purchase.id)
        self.assertEqual(fetched.first(), purchase)

        self.assertEqual(fetched, ArrangementPurchase.objects.filter(
            id=1
        ))

When I run this test the very last assert fails with the following error:

AssertionError: <QuerySet [<ArrangementPurchase: 1 : user_0 - Profound bandwidth-monitored pricing structure (vanaf 2019-04-24)>]> != <QuerySet [<ArrangementPurchase: 1 : user_0 - Profound bandwidth-monitored pricing structure (vanaf 2019-04-24)>]>

I've verified that my ArrangementPurchaseFactory is subclassing the DjangoModelFactory (as can be seen below)

class ArrangementPurchaseFactory(factory.django.DjangoModelFactory):

    class Meta:
        model = arrangement_models.ArrangementPurchase

    user = factory.SubFactory(UserFactory)
    arrangement_period = factory.SubFactory(ArrangementPeriodFactory)
    purchase_date = factory.LazyFunction(
        lambda: timezone.now() - datetime.timedelta(days=10)
    )
    expire_date = factory.LazyFunction(
        lambda: timezone.now() + datetime.timedelta(days=30)
    )
    tenant_demo_purchase = False
    price_paid = factory.LazyFunction(lambda: Decimal(0))
    linked_order_id = factory.Faker('sha1')
    rabo_purchase_pending = False

As far as I can tell the object in both querysets exists in the database (the object has an id value), and the pk value of the fetched query matches the existing purchase.id

So why is the test failing? Does anyone know what I am missing?


Solution

  • It's because though the values of these querySets are equal, they are actually different objects.

    What you need is assertQuerysetEqual. From the docs:

    TransactionTestCase.assertQuerysetEqual(qs, values, transform=repr, ordered=True, msg=None)[https://docs.djangoproject.com/en/2.2/topics/testing/tools/] Asserts that a queryset qs returns a particular list of values values.

    The comparison of the contents of qs and values is performed using the function transform; by default, this means that the repr() of each value is compared. Any other callable can be used if repr() doesn’t provide a unique or helpful comparison.

    By default, the comparison is also ordering dependent. If qs doesn’t provide an implicit ordering, you can set the ordered parameter to False, which turns the comparison into a collections.Counter comparison. If the order is undefined (if the given qs isn’t ordered and the comparison is against more than one ordered values), a ValueError is raised.

    Output in case of error can be customized with the msg argument.