Overview

factory_boy

factory_boy は thoughtbot の factory_girl をベースにした fixture replacement です。 factory_girl のように、わかりやすい定義構文、 複数のビルドストラテジ(インスタンスの保存、非保存、属性辞書、スタブオブジェクト)の対応、 ファクトリの継承を含め、同一クラスに対する複数ファクトリの対応などがあります。 Django に対応していますが、他の ORM の対応も簡単に追加することができます。

クレジット

この README はできるだけ factory_girl の README に対応させています。 文章や例は比較しやすいように同じ内容にしています。 factory_girl を使っている Ruby ユーザーなら、すぐに Python の factory_boy を使えるでしょう。

factory_boy は Mark Sandstrom によって書かれました。

Joe Ferris と factory_girl を作ってくれた thoughtbot に感謝します。

ダウンロード

Github: http://github.com/dnerdy/factory_boy/tree/master

easy_install:

easy_install factory_boy

ソース:

# ソースをダウンロードして実行
python setup.py install

ファクトリの定義

ファクトリは、オブジェクトをインスタンス化するのに使う属性のまとまりを宣言したものです。 ファクトリ名はデフォルトではオブジェクトのクラスを推測するのに使われますが、 明示的に指定することも可能です:

import factory
from models import User

# これは User クラスを推測します
class UserFactory(factory.Factory):
    first_name = 'John'
    last_name = 'Doe'
    admin = False

# これは(Admin クラスを推測しますが) User クラスを使います
class AdminFactory(factory.Factory):
    FACTORY_FOR = User

    first_name = 'Admin'
    last_name = 'User'
    admin = True

ファクトリの使用

factory_boy はいくつかの異なったビルドストラテジに対応しています: build, create, attributes そして stub です:

# save されていない User インスタンスを返します
user = UserFactory.build()

# save された User インスタンスを返します
user = UserFactory.create()

# User インスンタンスの生成に使える属性の辞書を返します
attributes = UserFactory.attributes()

# 全ての属性がスタブ化されたオブジェクトを返します(?)
stub = UserFactory.stub()

ファクトリクラスをデフォルトのビルドストラテジのショートカットとして使うことができます:

# UserFactory.create() と同じ
user = UserFactory()

デフォルトのストラテジを上書きすることも可能です:

UserFactory.default_strategy = factory.BUILD_STRATEGY
user = UserFactory()

全ファクトリに対するデフォルトストラテジを上書きすることもできます:

# デフォルトのビルドストラテジが定義されていない全ファクトリのデフォルトストラテジを設定
factory.Factory.default_strategy = factory.BUILD_STRATEGY

どのストラテジが使われていても、 キーワード引数を渡すことで既に定義済の属性を上書きすることが可能です:

# User インスタンスを生成して first_name を上書きする
user = UserFactory.build(first_name='Joe')
user.first_name
# => 'Joe'

遅延評価属性

ほとんどのファクトリ属性は、ファクトリを定義する際に評価される 静的な値を使ってファクトリに追加することができますが、 いくつかの属性(関連や動的に生成されるべき属性など)は インスタンスが生成される度に値が設定される必要があります。 そのような "lazy" な属性は、次のように追加することができます:

class UserFactory(factory.Factory):
    first_name = 'Joe'
    last_name = 'Blow'
    email = factory.LazyAttribute(lambda a: '{0}.{1}@example.com'.format(a.first_name, a.last_name).lower())

UserFactory().email
# => 'joe.blow@example.com'

LazyAttribute に渡された関数には、その宣言までにファクトリに定義された属性が与えられます。

ラムダ式では具合が悪い場合は lazy_attribute デコレータで関数を装飾することができます:

# Stub ファクトリは特定のクラスとのひもづきを持たない
class SumFactory(factory.StubFactory):
    lhs = 1
    rhs = 1

    @lazy_attribute
    def sum(a):
        result = a.lhs + a.rhs  # などのなんらかの計算
        return result

関連

LazyAttribute を使うことで、ひもづいたオブジェクトインスタンスを生成することもできます:

from models import Post

class PostFactory(factory.Factory):
    author = factory.LazyAttribute(lambda a: UserFactory())

ひもづいたオブジェクトでは、常にデフォルトストラテジが使われます:

# User と Post をビルドして保存する
post = PostFactory()
post.id == None           # => False
post.author.id == None    # => False

# User をビルドして保存してから Post をビルドするが保存はしない
post = PostFactory.build()
post.id == None           # => True
post.author.id == None    # => False

継承

継承を使えば、何度も共通の属性の宣言をしなくても、簡単に同じクラスのファクトリを複数作ることができます:

class PostFactory(factory.Factory):
    title = 'A title'

class ApprovedPost(PostFactory):
    approved = True
    approver = factory.LazyAttribute(lambda a: UserFactory())

シーケンス

特定のフォーマット(Eメールアドレスなど)のユニークな値はシーケンスを使って生成できます。 シーケンスは Sequence で定義するか sequence デコレータを使います:

class UserFactory(factory.Factory):
    email = factory.Sequence(lambda n: 'person{0}@example.com'.format(n))

UserFactory().email  # => 'person0@example.com'
UserFactory().email  # => 'person1@example.com'

シーケンスは遅延属性と組み合わせて使うこともできます:

class UserFactory(factory.Factory):
    name = 'Mark'
    email = factory.LazyAttributeSequence(lambda a, n: '{0}+{1}@example.com'.format(a.name, n).lower())

UserFactory().email  # => mark+0@example.com