Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Correct way to unit test Django models without transaction errors

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);
like image 641
JohnWoltman Avatar asked Nov 24 '25 17:11

JohnWoltman


1 Answers

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.

like image 188
jscn Avatar answered Nov 27 '25 08:11

jscn



Donate For Us

If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!