LoginSignup
21
10

More than 1 year has passed since last update.

テストコードを書く前にfactory_boyを学ぶべき

Last updated at Posted at 2020-12-27

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のメタクラスにはモデル名を指定します🥺

client.py
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で実際に使用してみる🥺

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)
21
10
1

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
21
10