Django / FactoryBoy - Равные запросы не равны? - PullRequest
0 голосов
/ 24 апреля 2019

Я пишу тесты для приложения 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)>]>

I 'мы убедились, что мой 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

Насколько я могу судить, объект в обоих наборах запросов существует в базе данных (объект имеет идентификаторзначение), а значение pk запроса fetched соответствует существующему purchase.id

Так почему же тест не пройден?Кто-нибудь знает, чего мне не хватает?

Ответы [ 2 ]

2 голосов
/ 24 апреля 2019

Это потому, что, хотя значения этих наборов запросов равны, на самом деле это разные объекты.

Вам нужно assertQuerysetEqual.Из документов:

TransactionTestCase.assertQuerysetEqual (qs, значения, transform = repr, order = True, msg = None) [https://docs.djangoproject.com/en/2.2/topics/testing/tools/] утверждает, что набор запросовqs возвращает определенный список значений значений.

Сравнение содержимого qs и значений выполняется с использованием функции transform;по умолчанию это означает, что repr () каждого значения сравнивается.Любой другой вызываемый объект может использоваться, если repr () не обеспечивает уникальное или полезное сравнение.

По умолчанию сравнение также зависит от порядка.Если qs не обеспечивает неявного упорядочения, вы можете установить для упорядоченного параметра значение False, что превращает сравнение в сравнение с коллекциями.Если порядок не определен (если заданные qs не упорядочены и сравнение выполняется с несколькими упорядоченными значениями), возникает ошибка ValueError.

Вывод в случае ошибки можно настроить с помощью аргумента msg.

1 голос
/ 24 апреля 2019

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__ сравнивает содержимое списков вместо их адресов памяти.

NB. Если в списках более 1 элемента, вам нужно будет принять во внимание порядок.

Другой вариант будет использовать assertQuerysetEqual: https://docs.djangoproject.com/en/2.2/topics/testing/tools/#django.test.TransactionTestCase.assertQuerysetEqual

...