Testing with Django

Note

To ensure you are using compatible versions, install with the testfixtures[django] extra.

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

class SampleModel(models.Model):
    value = models.IntegerField()
    not_editable = models.IntegerField(editable=False)
    created = models.DateTimeField(auto_now_add=True)
>>> SampleModel(id=1, value=1) == SampleModel(id=1, value=2)
True

To work around this, when Django is installed, a comparer for the Model class is automatically registered with ignore_eq=True, so compare() does the right thing:

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

same:
['id']

values differ:
'value': 1 != 2

Ignoring fields

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_fields=['value'])

Comparing non-editable fields

By default, non-editable fields are ignored:

>>> 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:

>>> 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', '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!