#factory_boyって何?
今回はDjangoでテストデータを作成したい!といった時のために
便利なfactory_boyを使ってテストデータを作る方法をまとめました🥺
と言うことで、factory_boyはテストデータを簡単に作るためのライブラリです🥺
RailsのFactory Girlをモデルが元になっている🥺
factory_boy Reference
#初期設定
pip install factory_boy
##フォルダ構成
factoriesフォルダーには、テスト使用するでclientオブジェクトを作成するコードを書きます🥺
test__client.pyファイルには、clientモデルのテストコードを書きます🥺
common/
├ tests/
│ └ factories/
│ ├_init__.py
│ └client.py
│ └test_models/
│ ├_init__.py
└ └test__client.py
#factory_boyの使い方
*factory_boyは、ORMを介してオブジェクトを作成します🥺
*factoryのメタクラスにはモデル名を指定します🥺
import factory
from common.models import Client
class ClientFactory(factory.django.DjangoModelFactory):
class Meta:
model = 'common.Client'
django_get_or_create = ('name',)
name = 'suzuki taro'
FactoryのMetaに書いてある、django_get_or_createは
Django組み込みのClient.objects.get_or_create
を意味している🥺
##オブジェクトを作成する
Clientオブジェクトを作成してみる🥺
>> ClientFactory()
<Client: suzuki taro>
>> ClientFactory(name='sato hanako')
>> Client.objects.all()
[<Client: suzuki taro>, <Client: sato hanako>]
ClientFactory.build()
を使うことで オーバーライドすることも可能になる🥺
>>> client_obj = ClientFactory.build(name='takahashi kotaro')
>>> client_obj.name
"takahashi kotaro"
##複数のオブジェクトを作成する
>>> clients = ClientFactory.create_batch(3, name=factory.Sequence(lambda n: 'Name {0}'.format(n)))
>>> len(clients)
3
>>> [client.name for client in clients]
['Name 0', 'Name 1', 'Name 2']
create_batch
を使うことで、オブジェクトを複数作成することができます、
factoryはモデルのテストで使用することができます🥺
tests/test_models/test__client.py
で実際に使用してみる🥺
from django.test import TestCase
from ..factories.client import ClientFactory
class ClientModelsTestCase(TestCase):
@classmethod
def setUpTestData(cls):
cls.Client = ClientFactory._meta.model
def test_crud_client(self):
# create
client = ClientFactory(name='suzuki taro')
# read
self.assertEqual(client.name, 'suzuki taro')
self.assertEqual(client.pk, client.id)
self.assertQuerysetEqual(
self.Client.objects.all(),
['<Client: suzuki taro>']
)
# update
client.last_name = 'takahashi kotaro'
client.save()
self.assertQuerysetEqual(
self.Client.objects.all(),
['<Client: takahashi kotaro>']
)
#factory_boyの特徴
・lambda関数を使用して Sequence
オブジェクトを使用し、一意のフィールド値を動的に作成します🥺
name = factory.Sequence(lambda n: 'Client %d' % n)
##LazyAttribute
LazyAttribute
は他のフィールドを元にデータを作成することができます🥺
email = factory.LazyAttribute(lambda obj: '%s@gmail.com' % obj.name)
##Sequences
特定のオブジェクトで一意のデータで複数のインスタンスを作成する場合は、
factory.Sequence
を追加することで可能です🥺
Sequence
は、0からの連番で値を入れていきます🥺
そのため毎回ユニークのオブジェクトを作成してくれる🥺
import factory
class ClientFactory(factory.django.DjangoModelFactory):
class Meta:
model = 'common.Client'
django_get_or_create = ('name',)
name = factory.Sequence(lambda n: 'Client %d' % n)
##SubFactories
ForeignKeyの1対多の関係に対しては、SubFactory
で対応できます🥺
import factory
class ClientProfileFactory(factory.django.DjangoModelFactory):
class Meta:
model = 'common.ClientProfile'
age = random.randint(0, 200)
client = factory.SubFactory(ClientFactory)
##Many-to-many
many-to-manyは、post_generation
を使います🥺
class ClientFactory(factory.django.DjangoModelFactory):
class Meta:
model = 'common.Client'
django_get_or_create = ('name',)
name = factory.Sequence(lambda n: 'Client %d' % n)
@factory.post_generation
def phones(self, create, extracted, **kwargs):
if not create:
return
if extracted:
for phone in extracted:
self.phones.add(phone)
モデルテストを書いていくとこのようになります🥺
client_phones = ClientPhoneFactory.create_batch(3, phone=factory.Sequence(lambda n: '{0}{0}{0}-{0}{0}{0}-{0}{0}{0}'.format(n)))
client = ClientFactory(name='suzuki taro', phones=client_phones)