Django / FactoryBoy - 相等的查询不相等?



我正在为 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)>]>

我已经验证了我的ArrangementPurchaseFactory正在子类化DjangoModelFactory(如下所示)

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

那么为什么测试失败呢?有谁知道我错过了什么?

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

你需要的是assertQuerysetEqual.从文档中:

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

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

默认情况下,比较也取决于顺序。如果 qs 没有 提供隐式排序,可以将排序参数设置为 如果为 false,这会将比较转换为集合。计数器 比较。如果顺序未定义(如果给定的 qs 未排序 并且与多个有序值进行比较),a 引发值错误。

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

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__方法比较列表的内容而不是它们的内存地址。

注意:如果列表包含 1 个以上的项目,则必须考虑排序。

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

最新更新