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



#factory_boyって何？
今回はDjangoでテストデータを作成したい！といった時のために
便利なfactory_boyを使ってテストデータを作る方法をまとめました🥺


と言うことで、factory_boyはテストデータを簡単に作るためのライブラリです🥺
RailsのFactory Girlをモデルが元になっている🥺

[factory_boy Reference](https://factoryboy.readthedocs.io/en/stable/index.html)
#初期設定
```terminal:
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のメタクラスにはモデル名を指定します🥺


```py: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オブジェクトを作成してみる🥺

```py:
>> ClientFactory()                   
<Client: suzuki taro>

>> ClientFactory(name='sato hanako')
>> Client.objects.all()
[<Client: suzuki taro>, <Client: sato hanako>]
```

`ClientFactory.build()`を使うことで オーバーライドすることも可能になる🥺


```py:
>>> client_obj = ClientFactory.build(name='takahashi kotaro')
>>> client_obj.name
    "takahashi kotaro"
```

##複数のオブジェクトを作成する
```py:
>>> 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`で実際に使用してみる🥺

```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`オブジェクトを使用し、一意のフィールド値を動的に作成します🥺

```py
name = factory.Sequence(lambda n: 'Client %d' % n)
```

##LazyAttribute
`LazyAttribute`は他のフィールドを元にデータを作成することができます🥺

```py 
email = factory.LazyAttribute(lambda obj: '%s@gmail.com' % obj.name)
```

##Sequences
特定のオブジェクトで一意のデータで複数のインスタンスを作成する場合は、 
`factory.Sequence`を追加することで可能です🥺
`Sequence`は、0からの連番で値を入れていきます🥺
そのため毎回ユニークのオブジェクトを作成してくれる🥺

```py
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`で対応できます🥺

``` py
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`を使います🥺

```py
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)
```
モデルテストを書いていくとこのようになります🥺

```py
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)
```