23
13

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 3 years have passed since last update.

DjangoAdvent Calendar 2020

Day 23

テストコードを書く前に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)
23
13
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
23
13

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?