Search code examples
pythondjangodjango-testing

Django's assertQuerysetEqual() method failing despite the two query sets printing out the same in the shell?


I have a Family and a Session with a one-to-many relationship, such that an instance family has a session_set. Further, the Session has a session_number field, which is an integer.

I have two instances of Family, family1 and family2, such that if I print out their session_set.order_by('session_number') in the ipdb debugger I get that they look exactly the same:

ipdb> family1.session_set.order_by('session_number')
<QuerySet [<Session: Welcome>, <Session: First-Time Parents: The Basics of Birth>, <Session: Initial Postpartum Lactation>, <Session: Sleep Techniques for New Babies>, <Session: Breastfeeding Preparation>, <Session: Newborn Care Basics>, <Session: Easing the Transition Back to Work>, <Session: Preparing for Parenting>, <Session: Decoding Baby Cues>, <Session: Postpartum Doula Support>, <Session: First-Time Parents: Birth Prep Q&A>, <Session: Postpartum Lactation Follow-Up>, <Session: Sleep Training for 4 Months & Beyond>, <Session: Mental Wellness in Pregnancy>, <Session: Infant CPR>, <Session: Prenatal Pelvic Physical Therapy>, <Session: Prenatal Massage>]>
ipdb> family2.session_set.order_by('session_number')
<QuerySet [<Session: Welcome>, <Session: First-Time Parents: The Basics of Birth>, <Session: Initial Postpartum Lactation>, <Session: Sleep Techniques for New Babies>, <Session: Breastfeeding Preparation>, <Session: Newborn Care Basics>, <Session: Easing the Transition Back to Work>, <Session: Preparing for Parenting>, <Session: Decoding Baby Cues>, <Session: Postpartum Doula Support>, <Session: First-Time Parents: Birth Prep Q&A>, <Session: Postpartum Lactation Follow-Up>, <Session: Sleep Training for 4 Months & Beyond>, <Session: Mental Wellness in Pregnancy>, <Session: Infant CPR>, <Session: Prenatal Pelvic Physical Therapy>, <Session: Prenatal Massage>]>

However, if I input these query sets into assertQuerysetEqual, I get a test failure:

ipdb> self.assertQuerysetEqual(family1.session_set.order_by('session_number'), family2.session_set.order_by('session_number'))
*** AssertionError: Lists differ: ['<Session: Welcome>', '<Session: First-Ti[642 chars]ge>'] != [<Session: Welcome>, <Session: First-Time [608 chars]age>]

First differing element 0:
'<Session: Welcome>'
<Session: Welcome>

Diff is 2186 characters long. Set self.maxDiff to None to see it.

Here is a more detailed comparison with maxDiff set to None:

ipdb> self.maxDiff = None
ipdb> self.assertQuerysetEqual(family1.session_set.order_by('session_number'), family2.session_set.order_by('session_number'))
*** AssertionError: Lists differ: ['<Session: Welcome>', '<Session: First-Ti[642 chars]ge>'] != [<Session: Welcome>, <Session: First-Time [608 chars]age>]

First differing element 0:
'<Session: Welcome>'
<Session: Welcome>

- ['<Session: Welcome>',
?  -                  -

+ [<Session: Welcome>,
-  '<Session: First-Time Parents: The Basics of Birth>',
?  -                                                  -

+  <Session: First-Time Parents: The Basics of Birth>,
-  '<Session: Initial Postpartum Lactation>',
?  -                                       -

+  <Session: Initial Postpartum Lactation>,
-  '<Session: Sleep Techniques for New Babies>',
?  -                                          -

+  <Session: Sleep Techniques for New Babies>,
-  '<Session: Breastfeeding Preparation>',
?  -                                    -

+  <Session: Breastfeeding Preparation>,
-  '<Session: Newborn Care Basics>',
?  -                              -

+  <Session: Newborn Care Basics>,
-  '<Session: Easing the Transition Back to Work>',
?  -                                             -

+  <Session: Easing the Transition Back to Work>,
-  '<Session: Preparing for Parenting>',
?  -                                  -

+  <Session: Preparing for Parenting>,
-  '<Session: Decoding Baby Cues>',
?  -                             -

+  <Session: Decoding Baby Cues>,
-  '<Session: Postpartum Doula Support>',
?  -                                   -

+  <Session: Postpartum Doula Support>,
-  '<Session: First-Time Parents: Birth Prep Q&A>',
?  -                                             -

+  <Session: First-Time Parents: Birth Prep Q&A>,
-  '<Session: Postpartum Lactation Follow-Up>',
?  -                                         -

+  <Session: Postpartum Lactation Follow-Up>,
-  '<Session: Sleep Training for 4 Months & Beyond>',
?  -                                               -

+  <Session: Sleep Training for 4 Months & Beyond>,
-  '<Session: Mental Wellness in Pregnancy>',
?  -                                       -

+  <Session: Mental Wellness in Pregnancy>,
-  '<Session: Infant CPR>',
?  -                     -

+  <Session: Infant CPR>,
-  '<Session: Prenatal Pelvic Physical Therapy>',
?  -                                           -

+  <Session: Prenatal Pelvic Physical Therapy>,
-  '<Session: Prenatal Massage>']
?  -                           -

+  <Session: Prenatal Massage>]

It seems like the comparisons are 'out of sync'. However, the session_number is also the same:

ipdb> [(session.session_number, str(session.session_type)) for session in family1.session_set.order_by('session_number')]
[(0, 'Welcome'), (1, 'First-Time Parents: The Basics of Birth'), (2, 'Initial Postpartum Lactation'), (3, 'Sleep Techniques for New Babies'), (4, 'Breastfeeding Preparation'), (5, 'Newborn Care Basics'), (6, 'Easing the Transition Back to Work'), (7, 'Preparing for Parenting'), (8, 'Decoding Baby Cues'), (9, 'Postpartum Doula Support'), (10, 'First-Time Parents: Birth Prep Q&A'), (11, 'Postpartum Lactation Follow-Up'), (12, 'Sleep Training for 4 Months & Beyond'), (13, 'Mental Wellness in Pregnancy'), (14, 'Infant CPR'), (15, 'Prenatal Pelvic Physical Therapy'), (16, 'Prenatal Massage')]
ipdb> [(session.session_number, str(session.session_type)) for session in family2.session_set.order_by('session_number')]
[(0, 'Welcome'), (1, 'First-Time Parents: The Basics of Birth'), (2, 'Initial Postpartum Lactation'), (3, 'Sleep Techniques for New Babies'), (4, 'Breastfeeding Preparation'), (5, 'Newborn Care Basics'), (6, 'Easing the Transition Back to Work'), (7, 'Preparing for Parenting'), (8, 'Decoding Baby Cues'), (9, 'Postpartum Doula Support'), (10, 'First-Time Parents: Birth Prep Q&A'), (11, 'Postpartum Lactation Follow-Up'), (12, 'Sleep Training for 4 Months & Beyond'), (13, 'Mental Wellness in Pregnancy'), (14, 'Infant CPR'), (15, 'Prenatal Pelvic Physical Therapy'), (16, 'Prenatal Massage')]

Why is assertQuerysetEqual not working as expected here?


Solution

  • This is because assertQuerysetEqual does not compare a queryset to another queryset, but instead compares a queryset to a list of values.

    From assertQuerysetEqual's documentation:

    TransactionTestCase.assertQuerysetEqual(qs, values, transform=repr, ordered=True, msg=None)

    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.

    So you should cast the second argument to a list instead:

    self.assertQuerysetEqual(family1.session_set.order_by('session_number'), list(family2.session_set.order_by('session_number')))