3

I'm writing some models with constraints like unique=True and blank=False and null=False. I'm trying to write tests for the models with nose. However, if I write a test like this:

from job_sites.models import Site, SiteType

@raises(IntegrityError)
def test_empty_site():
   s = Site()
   s.save()

@raises(IntegrityError)
def test_empty_site_type():
   st = SiteType()
   st.save()

I get a DatabaseError like this: DatabaseError: current transaction is aborted, commands ignored until end of transaction block after it runs the first test.

What is the correct way to run DJango model tests when I'm expecting errors?

For reference, the models look like this:

class SiteType(models.Model):
    site_type_id = models.AutoField(primary_key=True)
    site_type = models.CharField(max_length=32, unique=True, blank=False, null=False, default=None)
    site_type_abbrev = models.CharField(max_length=32, blank=True)

    class Meta:
        db_table = u'site_types'

class Site(models.Model):
    site_id = models.AutoField(primary_key=True, blank=False, null=False, db_index=True)
    site_name = models.CharField(max_length=128, blank=False, null=False, db_index=True)
    site_type = models.ForeignKey(SiteType, blank=True, null=True)
    date_entered = models.DateTimeField(auto_now_add=True)

    class Meta:
        db_table = u'sites'

My constraints and defaults look like this:

ALTER TABLE site_types ADD CONSTRAINT site_types_site_type_name_minlen CHECK (char_length(site_type) > 0);
ALTER TABLE sites ALTER COLUMN date_entered SET DEFAULT now();
ALTER TABLE sites ADD CONSTRAINT sites_site_name_minlen CHECK (char_length(site_name) > 0);

1 Answer 1

2

Instead of using nose's terse test definitions, you should create your tests as sub-classes of Django's TestCase. That way, your database etc will be set up and configured for you at runtime and all the transaction stuff will be magically taken care of.

There's an overview of how to write tests for Django projects at: https://docs.djangoproject.com/en/dev/topics/testing/overview/

The equivalent of what you're trying to do would look something like:

from django.db import IntegrityError
from django.utils import unittest
from job_sites.models import Site, SiteType

class TestMyStuff(unittest.TestCase):

    def test_empty_site(self):
        s = Site()
        assertRaises(IntegrityError, s.save())

    def test_empty_site_type(self):
        st = SiteType()
        assertRaises(IntegrityError, st.save())

(Disclaimer: I have not actually run this code so it might not work.)

However, it's probably a waste of time to test for this kind of thing. The only logic that's being tested here is internal to Django, so you're not learning anything about your application by testing it.

Sign up to request clarification or add additional context in comments.

3 Comments

I will try this. However, it is not a waste of time of time to test this because my constraints are implemented at the database level and I need to know that the database schema is correct for my application. I have found several bugs in automatically-generated code this way.
the kind of test you posted won't work with a PostgreSQL database. After the first IntegrityError is thrown the next attempt to call save() results in the same DatabaseError as my original question. There must be some accepted way to do unit testing with postgres.
Can I see your model definitions? When you say your "constraints are implemented at the database level", I'm not sure what you mean. Are you connecting to a pre-existing database or something?

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.