Testing when using django

Django’s ORM has an unfortunate implementation choice to consider Model instances to be identical as long as their primary keys are the same:

>>> from testfixtures.tests.test_django.models import SampleModel
>>> SampleModel(id=1, value=1) == SampleModel(id=1, value=2)
True

To work around this, testfixtures.django registers a comparer for the django Model class. However, for this to work, ignore_eq=True must be passed:

>>> from testfixtures import compare
>>> import testfixtures.django # to register the comparer...
>>> compare(SampleModel(id=1, value=1), SampleModel(id=1, value=2),
...         ignore_eq=True)
Traceback (most recent call last):
 ...
AssertionError: SampleModel not as expected:

same:
[u'id']

values differ:
'value': 1 != 2

Since the above can quickly become cumbersome, a django-specific version of compare(), with ignoring __eq__ built in, is provided:

>>> from testfixtures.django import compare as django_compare
>>> django_compare(SampleModel(id=1, value=1), SampleModel(id=1, value=2))
Traceback (most recent call last):
 ...
AssertionError: SampleModel not as expected:

same:
[u'id']

values differ:
'value': 1 != 2

It may also be that you want to ignore fields over which you have no control and cannot easily mock, such as created or modified times. For this, you can use the ignore_fields option:

>>> compare(SampleModel(id=1, value=1), SampleModel(id=1, value=2),
...         ignore_eq=True, ignore_fields=['value'])

Note

The implementation of the comparer for Model instances ignores fields that have editable set to False.

By default, non-editable fields are ignored:

>>> django_compare(SampleModel(not_editable=1), SampleModel(not_editable=2))

If you wish to include these fields in the comparison, pass the non_editable_fields option:

>>> django_compare(SampleModel(not_editable=1), SampleModel(not_editable=2),
...                non_editable_fields=True)
Traceback (most recent call last):
 ...
AssertionError: SampleModel not as expected:

same:
['created', u'id', 'value']

values differ:
'not_editable': 1 != 2

Note

The registered comparer currently ignores many to many fields. Patches to fix this deficiency are welcome!