问题
I'm just getting started with the factory_boy django library for test factories, and having an issue with a duplicate key constraint violation.
test_member_programme.py
from datetime import date, timedelta
from django.test import TestCase
from app.test.factories import MemberFactory, ProgrammeFactory
from app.models.member_programme import MemberProgramme
class MemberProgrammeTestCase(TestCase):
def member_programme(self):
yesterday = date.today() - timedelta(days=1)
return MemberProgramme.objects.create(
mem=MemberFactory(),
prg=ProgrammeFactory(),
date_registered=yesterday)
def date_registered_should_be_defined_test(self):
# This test passes
memprg = self.member_programme()
assert hasattr(memprg, 'date_registered')
def date_registered_should_be_in_past_test(self):
# This test fails
memprg = self.member_programme()
assert memprg.date_registered < date.today()
factories.py
class CountryOfOriginFactory(factory.Factory):
""" Factory class for app.models.CountryOfOrigin
"""
FACTORY_FOR = CountryOfOrigin
code = 'UK'
the_country = 'United Kingdom'
class MemberFactory(factory.Factory):
""" Factory class for app.models.Member
"""
FACTORY_FOR = Member
first_name = 'Test'
surname = 'User'
sex = 'M'
date_of_birth = datetime.date(1990, 1, 1)
origin = factory.LazyAttribute(lambda a: CountryOfOriginFactory())
When running the first test passes successfully, but the second fails with the following error:
IntegrityError: duplicate key value violates unique constraint "country_of_origin_code_key"
My understanding is that every TestCase should run within a transaction, yet the creation of the foreign key does not appear to have rolled back before the second test runs. Clearly I'm doing something fundamentally wrong, but I'm a bit stumped! Thanks!
I've tracked down the problem, but unfortunately don't know how to resolve it. The issue is that ROLLBACKs are occurring, but only on one database (this app has 2 databases). For legacy reasons, we have a separate database for django auth, flatpages etc and another db for our app.
dba test_app 127.0.0.1 2012-09-04 21:51:50.806 UTC LOG: duration: 0.038 ms statement: BEGIN; SET TRANSACTION ISOLATION LEVEL READ COMMITTED
dba test_app 127.0.0.1 2012-09-04 21:51:50.808 UTC LOG: duration: 0.903 ms statement: INSERT INTO "member_programme" ("mem_id", "prgm_id", "date_registered", "date_completed", "ordinality") VALUES (1, 1, E'2012-09-04', NULL, 1)
dba test_app 127.0.0.1 2012-09-04 21:51:50.808 UTC LOG: duration: 0.150 ms statement: SELECT CURRVAL(pg_get_serial_sequence('"member_programme"','id'))
dba test_app 127.0.0.1 2012-09-04 21:51:50.810 UTC LOG: duration: 1.796 ms statement: COMMIT
dba test_app_django 127.0.0.1 2012-09-04 21:51:50.811 UTC LOG: duration: 0.056 ms statement: ROLLBACK <---- ROLLBACK ON DJANGO DB ONLY
dba test_app_django 127.0.0.1 2012-09-04 21:51:50.814 UTC LOG: disconnection: session time: 0:00:21.005 user=dba database=test_app_django host=127.0.0.1 port=60355
dba test_app 127.0.0.1 2012-09-04 21:51:50.818 UTC LOG: disconnection: session time: 0:00:04.751 user=dba database=test_app host=127.0.0.1 port=60357
dba test_app 127.0.0.1 2012-09-04 21:54:00.796 UTC LOG: connection authorized: user=dba database=test_app
dba test_app 127.0.0.1 2012-09-04 21:54:00.802 UTC LOG: duration: 0.243 ms statement: SET DATESTYLE TO 'ISO'
dba test_app 127.0.0.1 2012-09-04 21:54:00.802 UTC LOG: duration: 0.156 ms statement: SHOW client_encoding
dba test_app 127.0.0.1 2012-09-04 21:54:00.803 UTC LOG: duration: 0.047 ms statement: SHOW default_transaction_isolation
dba test_app 127.0.0.1 2012-09-04 21:54:00.803 UTC LOG: duration: 0.068 ms statement: BEGIN; SET TRANSACTION ISOLATION LEVEL READ COMMITTED
dba test_app 127.0.0.1 2012-09-04 21:54:00.804 UTC LOG: duration: 0.410 ms statement: SET TIME ZONE E'Pacific/Auckland'
dba test_app 127.0.0.1 2012-09-04 21:54:00.805 UTC ERROR: duplicate key value violates unique constraint "country_of_origin_code_key"
Someone with a similar problem here.
回答1:
Django has had support for testing against multiple databases since 1.2!
Adding the following property to my TestCase resolved the issue:
multi_db = True
回答2:
Ha! I think I found it after re-reading your question.
Your factory class is defining origin
as part of its default value during the build. But we're not passing it any value in the setup hence the increment. So to fix this, one could do the following:
FACTORY CLASSES
class CountryOfOriginFactory(factory.Factory):
""" Factory class for app.models.CountryOfOrigin
"""
FACTORY_FOR = CountryOfOrigin
code = 'UK'
the_country = 'United Kingdom'
class MemberFactory(factory.Factory):
""" Factory class for app.models.Member
"""
FACTORY_FOR = Member
first_name = 'Test'
surname = 'User'
sex = 'M'
date_of_birth = datetime.date(1990, 1, 1)
origin = factory.LazyAttribute(lambda a: CountryOfOriginFactory())
class MemberProgramme(factory.Factory):
FACTORY_FOR = MemberProgramme
mem = factory.LazyAttribute(lambda a: MemberFactory())
prg = factory.LazyAttribute(lambda a: ProgrammeFactory())
date_registered = date.today() - timedelta(days=1)
TEST
class MemberProgrammeTestCase(TestCase):
def setUp(self):
self.prog = ProgrammeFactory()
self.country_of_origin = CountryOfOriginFactory()
self.member = MemberFactory(origin=self.country_of_origin)
self.member_programme = MemberProgramme(mem=self.mem, prg=self.prog)
def date_registered_should_be_defined_test(self):
# This test passes
memprg = self.member_programme
assert hasattr(memprg, 'date_registered')
def date_registered_should_be_in_past_test(self):
# This test fails
memprg = self.member_programme
assert memprg.date_registered < date.today()
来源:https://stackoverflow.com/questions/12205855/lack-of-rollback-within-testcase-causes-unique-contraint-violation-in-multi-db-d