Django / FactoryBoy-等于查询等于?

时间:2019-04-24 10:06:38

标签: python django factory-boy

我正在为Django应用程序编写测试,但我的测试之一失败了,而一个奇怪的错误是assertEqual比较失败,即使两个查询集中的对象都匹配。

测试很大,所以我写了一个小测试来重现错误:

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
        ))

运行此测试时,最后一个断言失败并显示以下错误:

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)>]>

我已验证我的ArrangementPurchaseFactoryDjangoModelFactory的子类(如下所示)

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

据我所知,两个查询集中的对象都存在于数据库中(该对象具有id值),fetched查询的pk值与现有的purchase.id

那为什么测试失败了?有人知道我在想什么吗?

2 个答案:

答案 0 :(得分:2)

这是因为尽管这些querySet的值相等,但实际上它们是不同的对象。

您需要的是assertQuerysetEqual。从文档中:

  

TransactionTestCase.assertQuerysetEqual (qs,值,transform = repr,   ordered = True,msg = None)[https://docs.djangoproject.com/en/2.2/topics/testing/tools/]断言查询集qs返回a   值的特定列表。

     

比较qs和值的内容是使用   功能变换;默认情况下,这意味着每个的repr()   比较值。如果repr()不存在,则可以使用任何其他可调用对象   提供独特或有用的比较。

     

默认情况下,比较也取决于顺序。如果qs没有   提供隐式排序,您可以将ordered参数设置为   否,将比较结果转换为集合。   比较。如果顺序未定义(如果给定的q不排序   并且比较是针对多个订购值的)   引发ValueError。

     

发生错误时的输出可以使用msg参数自定义。

答案 1 :(得分:1)

Django在QuerySet实例之间不提供任何特定的相等性比较(请参见代码:https://github.com/django/django/blob/master/django/db/models/query.py#L185)。

如果未提供自定义__eq__方法,Python将退回比较内存中的对象地址。 QuerySet的两个不同实例即使使用相同的参数构建,也将具有不同的地址,并且比较不相等。

如果要比较查询集的内容(即数据库中对象的列表),则必须对其进行评估,例如将其转换为列表:

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

使用此方法,Python将比较列表,其__eq__方法比较列表的内容而不是它们的内存地址。

注意:如果列表中有多个项目,则必须考虑订购。

另一种选择是使用assertQuerysetEqualhttps://docs.djangoproject.com/en/2.2/topics/testing/tools/#django.test.TransactionTestCase.assertQuerysetEqual