#python #django #factory-boy
#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)>]>
Я проверил, что мой 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), а значение pk fetched
запроса соответствует существующему purchase.id
Итак, почему тест не выполняется? Кто-нибудь знает, чего мне не хватает?
Ответ №1:
Это потому, что, хотя значения этих наборов запросов равны, на самом деле это разные объекты.
Что вам нужно assertQuerysetEqual
. Из документов:
TransactionTestCase.assertQuerysetEqual(qs, values, transform=repr, ordered= True, msg=None)[https://docs.djangoproject.com/en/2.2/topics/testing/tools /] Утверждает, что набор запросов qs возвращает определенный список значений values.
Сравнение содержимого qs и значений выполняется с использованием функции transform; по умолчанию это означает, что сравнивается repr() каждого значения. Можно использовать любой другой вызываемый параметр, если repr() не предоставляет уникального или полезного сравнения.
По умолчанию сравнение также зависит от порядка. Если qs не предоставляет неявный порядок, вы можете установить параметру ordered значение False, что превращает сравнение в коллекции.Сравнение счетчиков. Если порядок не определен (если данный qs не упорядочен и сравнение проводится с более чем одним упорядоченным значением), возникает ошибка значения.
Вывод в случае ошибки может быть настроен с помощью аргумента msg.
Ответ №2:
Django не предоставляет никакого конкретного сравнения равенства между QuerySet
экземплярами (см. Код:https://github.com/django/django/blob/master/django/db/models/query.py#L185).
Когда пользовательский __eq__
метод не предоставляется, Python возвращается к сравнению адресов объектов в памяти. Два разных экземпляра a 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
Комментарии:
1. Спасибо, Xelnor! Это действительно полезно знать и просто помогло мне!