Question

I have a Django model with a "title" CharField(unique=True). I have a unit test that asserts that creating a second instance with the same title throws an IntegrityError. (I'm using pytest and pytest-django.)

I have something like:

class Foo(models.Model):
    title = models.CharField(unique=True)

def test_title_is_unique(db):
    Foo.objects.create(title='foo')
    with pytest.raises(IntegrityError):
        Foo.objects.create(title='foo')

This works fine, except the above code doesn't include cleanup code. pytest-django doesn't clean up the database for you, so you need to register cleanup handlers when you create or save a model instance. Something like this:

def test_title_is_unique(request, db):
    foo = Foo.objects.create(title='foo')
    request.addfinalizer(foo.delete)
    with pytest.raises(IntegrityError):
        Foo.objects.create(title='foo')

Okay, that's fine. But what if the second .create() call erroneously succeeds? I still want to clean up that instance, but only if it (erroneously) gets created.

Here is what I settled on:

def test_title_is_unique(request, db):
    foo = Foo.objects.create(title='foo')
    request.addfinalizer(foo.delete)
    try:
        with pytest.raises(IntegrityError):
            new_foo = Foo.objects.create(title='foo')
    finally:
        if 'new_foo' in locals():
            request.addfinalizer(new_foo.delete)

This doesn't feel particularly elegant or Pythonic, not to mention there are a bunch of lines of code that really shouldn't be running.

How do I guarantee that the second model instance is cleaned up if created, but with fewer hoops to jump through, and/or using fewer lines of code?

Was it helpful?

Solution

You should not need to worry about the cleanup. pytest-django's db fixture disables transactions for the test, executing the entire test in one transaction and rolling back the transaction at the end. This ensures the database remains clean.

If the test requires transaction there's the transactional_db fixture which will enable transactions (slower) and flush the entire contents of the db after the test (very slow) again cleaning up for you.

So if the cleanup does not happen then you should probably file a bug at pytest-django. But I would be surprised if this is the case unless I missed something important.

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top