On Mon, Apr 29, 2013 at 4:08 AM, Danilo Bargen <[email protected]> wrote:

> Hi all
>
> Today I ran across an issue while debugging Django tests. As stated in the
> docs, all model changes in a TestCase that are done during the test are
> reverted between the tests. This creates a nice and useful isolation of the
> different tests. Each time a test runs, the database looks just the way it
> was after the syncdb.
>
> > from django.test import TestCase
> > from apps.front import models
> >
> > class Test1(TestCase):
> >     def setUp(self):
> >         models.User.objects.create(username='spamham')
> >
> >     def test(self):
> >         pass
> >
> > class Test2(TestCase):
> >     def setUp(self):
> >         models.User.objects.create(username='spamham')
> >
> >     def test(self):
> >         pass
>
> These test run fine, even though two users with the same PK are created,
> because Django handles test isolation. But things behave differently when
> using the setUpClass classmethod. I used setUpClass instead of setUp
> because there are certain models that I just want to create once for all
> the tests in a test case. It would be an unnecessary performance loss if
> those model instances were created and then rolled back for each test
> method. And less DRY.
>
> The problem is, model changes that are done in setUpClass aren't rolled
> back between the test cases.
>
> > from django.test import TestCase
> > from apps.front import models
> >
> > class Test1(TestCase):
> >     @classmethod
> >     def setUpClass(cls):
> >         models.User.objects.create(username='spamham')
> >
> >     def test(self):
> >         pass
> >
> > class Test2(TestCase):
> >     @classmethod
> >     def setUpClass(cls):
> >         models.User.objects.create(username='spamham')
> >
> >     def test(self):
> >         pass
>
> This fails with an IntegrityError due to a violation of
> the front_user_username_key unique constraint, because the User object from
> the first test wasn't removed.
>
> Is this a design decision? Or a technical limitation? (I realize that the
> use of a setup classmethod could cause issues with parallelization, but
> those issues could be addressed in the test runner.)
>

Neither really. It's an artefact of the history of Django's test framework.

The test framework was written almost 7 years ago, predating unittest2 by
several years. setUpClass was introduced by unittest2. As a result, the
infrastructure that resets tests after each setUp/tearDown pair hasn't been
modified to include setUpClass/tearDownClass as well.

I haven't looked at the problem in detail to establish if there is a
fundamental technical limitation preventing this; but off the top of my
head, I can think of a few complications that might pose difficulty. In
particular, the transaction rollback approach used to speed up fixture
loading won't adapt well to having two different 'setup' routines.


> And in the meantime, is there a better solution than to put all the common
> setup code in the setUp method?
>

Unfortuntately not.


> Note that I don't want to use fixtures for this. Fixtures are not a good
> way to create test data, as they're very static and need to be updated with
> the code. This also makes them error-prone. Instead I'm using Model Mommy (
> https://github.com/vandersonmota/model_mommy), a model factory library
> for Django. Another good alternative would be Factory Boy. These factories
> are also the reasons why I don't want to do manual cleanup in
> tearDownClass: First of all the model factory also creates related models
> that are needed to satisfy some foreign key constraints. I don't have
> references to all those model instances. And the second reason is that I
> think the database cleanup is something that Django should handle, not me.
>

If you want to avoid fixtures, Factory Boy would be my suggestion; I
haven't used Model Mommy myself, but it looks to be in much the same vein.

Yours,
Russ Magee %-)

-- 
You received this message because you are subscribed to the Google Groups 
"Django developers" group.
To unsubscribe from this group and stop receiving emails from it, send an email 
to [email protected].
To post to this group, send email to [email protected].
Visit this group at http://groups.google.com/group/django-developers?hl=en.
For more options, visit https://groups.google.com/groups/opt_out.


Reply via email to