我正在使用Factory Boy为我的django应用创建测试工厂。我遇到问题的模型是一个非常基本的帐户模型,它与django User auth模型具有OneToOne关系(使用django< 1.5):
# models.py
from django.contrib.auth.models import User
from django.db import models
class Account(models.Model):
user = models.OneToOneField(User)
currency = models.CharField(max_length=3, default='USD')
balance = models.CharField(max_length="5", default='0.00')
这是我的工厂:
# factories.py
from django.db.models.signals import post_save
from django.contrib.auth.models import User
import factory
from models import Account
class AccountFactory(factory.django.DjangoModelFactory):
FACTORY_FOR = Account
user = factory.SubFactory('app.factories.UserFactory')
currency = 'USD'
balance = '50.00'
class UserFactory(factory.django.DjangoModelFactory):
FACTORY_FOR = User
username = 'bob'
account = factory.RelatedFactory(AccountFactory)
所以每当调用AccountFactory时,我都希望工厂男孩能够创建一个相关的UserFactory:
# tests.py
from django.test import TestCase
from factories import AccountFactory
class AccountTest(TestCase):
def setUp(self):
self.factory = AccountFactory()
def test_factory_boy(self):
print self.factory.id
然而,在运行测试时,看起来正在创建多个用户模型,并且我看到了整合错误:
IntegrityError: column username is not unique
文档确实提到在处理循环导入时注意循环,但我不确定这是否正在进行,以及我将如何补救它。 docs
如果熟悉Factory Boy的任何人可以插入或提供有关可能导致此完整性错误的一些信息,那将非常感谢!
答案 0 :(得分:11)
我相信这是因为您的工厂定义中有循环引用。尝试从account = factory.RelatedFactory(AccountFactory)
定义中删除行UserFactory
。如果您总是通过AccountFactory调用帐户创建,那么您不应该需要这一行。
另外,您可以考虑将序列附加到名称字段,这样如果您确实需要多个帐户,它会自动生成它们。
将username = "bob"
更改为username = factory.Sequence(lambda n : "bob {}".format(n))
,您的用户将被命名为“bob 1”,“bob 2”等。
答案 1 :(得分:1)
要将调用 UserFactory
的结果传递给 AccountFactory
,您应该使用 factory_related_name
(docs)
以上代码的工作方式如下:
AccountFactory
用于实例化需求 SubFactory(UserFactory)
。UserFactory
实例化用户。UserFactory
实例化调用后 RelatedFactory(AccountFactory)
FuzzyText
或 Sequence
生成用户名)所以你需要像这样写UserFactory
:
class UserFactory(factory.django.DjangoModelFactory):
account = factory.RelatedFactory(AccountFactory, factory_related_name='user')
username = factory.Sequence(lambda a: 'email%04d@somedomain.com' % a)
# rest of code
但是您仍然会遇到已经编写好的测试的问题。想象一下,你在下一个测试地点:
user = UserFactory()
account = Account(user=user)
然后添加 RelatedFactory
会破坏测试。如果你的项目中没有很多测试和贡献者,你可以重写它们。但如果没有,这不是一个选择。处理方法如下:
class UserFactory(factory.django.DjangoModelFactory):
class Params:
generate_account = factory.Trait(
account=factory.RelatedFactory(AccountFactory, factory_related_name='user')
)
那么上面的代码不会被破坏,因为 UserFactory
的默认调用不会实例化 AccountFactory
。使用帐户实例化用户:
user_with_account = UserFactory(generate_account=True)