Fixtures won't load twice in same testcase

130 views
Skip to first unread message

Rich Rauenzahn

unread,
May 4, 2016, 2:26:48 PM5/4/16
to Django users

I'm having a strange problem.  My test environment has been working fine, but I am upgrading my environment's Django revision slowly, which means I also need to move away from django-nose's FastFixtureTestCase.

I'm now at Django 1.7.   I have a TestCase which is more or less...

class Foo(TestCase): # and I've tried TransactionTestCase

    fixtures = ['accounts.json', 'config.json', 'runtimedata.json']

    def test1(self):
        pass # yes, literally.

    def test2(self):
        pass # literally, pass right now


If I split test1 and test2 into two classes, the TestCase works fine.

Otherwise, my runtimedata app fixture ends up with a duplicate row for the second test during the fixture setup phase.   When I turned on postgres (9.2) logging, I get:

LOG:  statement: UPDATE "runtimedata_branch" SET "name" = 'mock' WHERE "runtimedata_branch"."id" = 1
LOG:  statement: INSERT INTO "runtimedata_branch" ("id", "name") VALUES (1, 'mock')
ERROR:  duplicate key value violates unique constraint "runtimedata_branch_name_49810fc21046d2e2_uniq"
DETAIL:  Key (name)=(mock) already exists.
STATEMENT:  INSERT INTO "runtimedata_branch" ("id", "name") VALUES (1, 'mock')
LOG:  statement: ROLLBACK
LOG:  statement: DROP DATABASE "test_db"

If I grep for runtimedata_branch in the postgres logs, I get ...starting after the TRUNCATE caused by TransactionTestCase ....

LOG:  statement: TRUNCATE [...], "runtimedata_branch", [...]; #
LOG:  statement: SELECT "runtimedata_branch"."id", "runtimedata_branch"."name" FROM "runtimedata_branch" WHERE "runtimedata_branch"."name" = 'mock' LIMIT 21
LOG:  statement: INSERT INTO "runtimedata_branch" ("name") VALUES ('mock') RETURNING "runtimedata_branch"."id"
LOG:  statement: UPDATE "runtimedata_branch" SET "name" = 'mock' WHERE "runtimedata_branch"."id" = 1
LOG:  statement: INSERT INTO "runtimedata_branch" ("id", "name") VALUES (1, 'mock')
ERROR:  duplicate key value violates unique constraint "runtimedata_branch_name_49810fc21046d2e2_uniq"
STATEMENT:  INSERT INTO "runtimedata_branch" ("id", "name") VALUES (1, 'mock')

You can see a different pattern for the first pass fixture loading for test1,

LOG:  statement: INSERT INTO "runtimedata_branch" ("name") VALUES ('mock') RETURNING "runtimedata_branch"."id"
LOG:  statement: UPDATE "runtimedata_branch" SET "name" = 'mock' WHERE "runtimedata_branch"."id" = 1

The main difference I can see is the first time, the tables are generated from scratch, the second the tables have been truncated.  Note, I get a very similar outcome with TestCase which does rollbacks instead of truncates.

Any ideas?  This is very odd.

Also, my fixtures are dumped with --natural.  I've just tried it without --natural and I get the same outcome.



Rich Rauenzahn

unread,
May 5, 2016, 6:42:23 PM5/5/16
to Django users

I've been tracing into django core code, and it looks to me that I have a case where the fixture has a auth.User(username=rich) with a pk=1 in the fixture.  But sometimes as the User fixture with pk=1 is being added (updated?) through Model._save_table(), the same User with pk=5 is already in the db (did not exist prior to fixture loading).  This causes the django core code (Model._save_table()) to also try to insert another user with the same username, causing an integrity error.

I added my own _fixture_setup() that asserted my db was clean prior to fixture loading.  It failed!  This lead me to find objects being created in a TestCase.setUpClass rather than TestCase.setUp(), and so were leaking across tests, fouling up fixture loading.

What's odd is that django-nose's FastFixtureTestCase hid this problem.  .... I think it affected the ordering and ran all the fixture based test cases first before the db was polluted.   (But isn't the DB dropped and refreshed after every TestCase?)


Mike Dewhirst

unread,
May 5, 2016, 7:22:11 PM5/5/16
to django...@googlegroups.com
On 6/05/2016 8:42 AM, Rich Rauenzahn wrote:
>
> I've been tracing into django core code, and it looks to me that I have
> a case where the fixture has a auth.User(username=rich) with a pk=1 in
> the fixture. Â But sometimes as the User fixture with pk=1 is being
> added (updated?) through Model._save_table(), the same User with pk=5 is
> already in the db (did not exist prior to fixture loading). Â This
> causes the django core code (Model._save_table()) to also try to insert
> another user with the same username, causing an integrity error.
>
> I added my own _fixture_setup() that asserted my db was clean prior to
> fixture loading. Â It failed! Â This lead me to find objects being
> created in a TestCase.setUpClass rather than TestCase.setUp(), and so
> were leaking across tests, fouling up fixture loading.
>
> What's odd is that django-nose's FastFixtureTestCase hid this problem.
> Â .... I think it affected the ordering and ran all the fixture based
> test cases first before the db was polluted. Â (But isn't the DB
> dropped and refreshed after every TestCase?)

Are you using setUp() and tearDown() as class methods in your test class?

>
>
> --
> You received this message because you are subscribed to the Google
> Groups "Django users" group.
> To unsubscribe from this group and stop receiving emails from it, send
> an email to django-users...@googlegroups.com
> <mailto:django-users...@googlegroups.com>.
> To post to this group, send email to django...@googlegroups.com
> <mailto:django...@googlegroups.com>.
> Visit this group at https://groups.google.com/group/django-users.
> To view this discussion on the web visit
> https://groups.google.com/d/msgid/django-users/c9d604a9-af10-4e22-91de-f81f3986d28d%40googlegroups.com
> <https://groups.google.com/d/msgid/django-users/c9d604a9-af10-4e22-91de-f81f3986d28d%40googlegroups.com?utm_medium=email&utm_source=footer>.
> For more options, visit https://groups.google.com/d/optout.

Alasdair Nicol

unread,
May 6, 2016, 7:11:42 AM5/6/16
to Django users
Hi Rich,

Regarding a couple of things you mentioned on Django-developers:

On Thursday, 5 May 2016 19:20:16 UTC+1, Rich Rauenzahn wrote:

Thanks, Tim.

Unfortunately I can't move past Django 1.7 yet -- dependencies.  I've been marching my way up one revision at a time hopefully up to 1.9 as a way to keep the scope of what breaks under control as I move through each major revision and stabilize my project.  Then I attack replacing dependencies.

I really think I've found a bug here ... which I hope to suggest a patch for and submit, hence the post to the developers channel, but I can go back to the users group for now... My recent experience with that list doesn't bode well, however, and I don't have high hopes with anyone there able to respond at the internals level I may need to track down the issue.  I've almost rewritten my tests to just load raw sql, but if there is a bug here I'd like to help find it rather than work around/ignore it.

Fixture loading was changed in Django 1.8 [1], so even if you have found a bug in Django 1.7, there's a good chance that behaviour will be different in Django 1.8 and later.


But In this particular run I'm currently tracing, rich is already in the db (as the only entry) as pk=5 (via fixture loading process).   For one, this tells me the sequence generators aren't always resetting between fixture loads/tests.

 
Sequences *are not* reset between test cases by default [2]. Perhaps you need to change your code so that it doesn't assume that the user has pk=1, or set reset_sequences = True.

It's difficult to offer any more advice, because you haven't posted any code that can reproduce the problem. It doesn't need to be the actual code, in fact the simpler the example is the better.

Cheers,
Alasdair

Rich Rauenzahn

unread,
May 12, 2016, 6:06:11 PM5/12/16
to Django users


On Thursday, May 5, 2016 at 4:22:11 PM UTC-7, Mike Dewhirst wrote:
Are you using setUp() and tearDown() as class methods in your test class?


No, the code was using setUpClass(), which is a classmethod. 

Rich Rauenzahn

unread,
May 12, 2016, 6:09:48 PM5/12/16
to Django users


On Friday, May 6, 2016 at 4:11:42 AM UTC-7, Alasdair Nicol wrote:


But In this particular run I'm currently tracing, rich is already in the db (as the only entry) as pk=5 (via fixture loading process).   For one, this tells me the sequence generators aren't always resetting between fixture loads/tests.

 
Sequences *are not* reset between test cases by default [2]. Perhaps you need to change your code so that it doesn't assume that the user has pk=1, or set reset_sequences = True.

My code didn't make that assumption -- it appeared that the fixture loading code did.
 

It's difficult to offer any more advice, because you haven't posted any code that can reproduce the problem. It doesn't need to be the actual code, in fact the simpler the example is the better.

I spent some cycles trying to reproduce on a fresh Django install, but couldn't based on my assumptions.  As I replied later, the problem was a setUpClass() creating objects via a factory that persisted between TestCase's that did fixture loading, and they conflicted.

Rich 
Reply all
Reply to author
Forward
0 new messages