diff options
Diffstat (limited to 'README.rst')
-rw-r--r-- | README.rst | 160 |
1 files changed, 133 insertions, 27 deletions
@@ -1,25 +1,78 @@ factory_boy =========== +.. image:: https://pypip.in/version/factory_boy/badge.svg + :target: http://factoryboy.readthedocs.org/en/latest/changelog.html + :alt: Latest Version + +.. image:: https://pypip.in/py_versions/factory_boy/badge.svg + :target: https://pypi.python.org/pypi/factory_boy/ + :alt: Supported Python versions + +.. image:: https://pypip.in/wheel/factory_boy/badge.svg + :target: https://pypi.python.org/pypi/factory_boy/ + :alt: Wheel status + .. image:: https://secure.travis-ci.org/rbarrois/factory_boy.png?branch=master :target: http://travis-ci.org/rbarrois/factory_boy/ factory_boy is a fixtures replacement based on thoughtbot's `factory_girl <http://github.com/thoughtbot/factory_girl>`_. -Its features include: +As a fixtures replacement tool, it aims to replace static, hard to maintain fixtures +with easy-to-use factories for complex object. -- Straightforward syntax -- Support for multiple build strategies (saved/unsaved instances, attribute dicts, stubbed objects) -- Powerful helpers for common cases (sequences, sub-factories, reverse dependencies, circular factories, ...) +Instead of building an exhaustive test setup with every possible combination of corner cases, +``factory_boy`` allows you to use objects customized for the current test, +while only declaring the test-specific fields: + +.. code-block:: python + + class FooTests(unittest.TestCase): + + def test_with_factory_boy(self): + # We need a 200€, paid order, shipping to australia, for a VIP customer + order = OrderFactory( + amount=200, + status='PAID', + customer__is_vip=True, + address__country='AU', + ) + # Run the tests here + + def test_without_factory_boy(self): + address = Address( + street="42 fubar street", + zipcode="42Z42", + city="Sydney", + country="AU", + ) + customer = Customer( + first_name="John", + last_name="Doe", + phone="+1234", + email="john.doe@example.org", + active=True, + is_vip=True, + address=address, + ) + # etc. + +factory_boy is designed to work well with various ORMs (Django, Mogo, SQLAlchemy), +and can easily be extended for other libraries. + +Its main features include: + +- Straightforward declarative syntax +- Chaining factory calls while retaining the global context +- Support for multiple build strategies (saved/unsaved instances, stubbed objects) - Multiple factories per class support, including inheritance -- Support for various ORMs (currently Django, Mogo, SQLAlchemy) Links ----- * Documentation: http://factoryboy.readthedocs.org/ -* Official repository: https://github.com/rbarrois/factory_boy +* Repository: https://github.com/rbarrois/factory_boy * Package: https://pypi.python.org/pypi/factory_boy/ factory_boy supports Python 2.6, 2.7, 3.2 and 3.3, as well as PyPy; it requires only the standard Python library. @@ -53,7 +106,8 @@ Usage Defining factories """""""""""""""""" -Factories declare a set of attributes used to instantiate an object. The class of the object must be defined in the FACTORY_FOR attribute: +Factories declare a set of attributes used to instantiate an object. +The class of the object must be defined in the ``model`` field of a ``class Meta:`` attribute: .. code-block:: python @@ -61,7 +115,8 @@ Factories declare a set of attributes used to instantiate an object. The class o from . import models class UserFactory(factory.Factory): - FACTORY_FOR = models.User + class Meta: + model = models.User first_name = 'John' last_name = 'Doe' @@ -69,7 +124,8 @@ Factories declare a set of attributes used to instantiate an object. The class o # Another, different, factory for the same object class AdminFactory(factory.Factory): - FACTORY_FOR = models.User + class Meta: + model = models.User first_name = 'Admin' last_name = 'User' @@ -79,7 +135,7 @@ Factories declare a set of attributes used to instantiate an object. The class o Using factories """"""""""""""" -factory_boy supports several different build strategies: build, create, attributes and stub: +factory_boy supports several different build strategies: build, create, and stub: .. code-block:: python @@ -89,8 +145,8 @@ factory_boy supports several different build strategies: build, create, attribut # Returns a saved User instance user = UserFactory.create() - # Returns a dict of attributes that can be used to build a User instance - attributes = UserFactory.attributes() + # Returns a stub object (just a bunch of attributes) + obj = UserFactory.stub() You can use the Factory class as a shortcut for the default build strategy: @@ -111,6 +167,42 @@ No matter which strategy is used, it's possible to override the defined attribut "Joe" +It is also possible to create a bunch of objects in a single call: + +.. code-block:: pycon + + >>> users = UserFactory.build_batch(10, first_name="Joe") + >>> len(users) + 10 + >>> [user.first_name for user in users] + ["Joe", "Joe", "Joe", "Joe", "Joe", "Joe", "Joe", "Joe", "Joe", "Joe"] + + +Realistic, random values +"""""""""""""""""""""""" + +Demos look better with random yet realistic values; and those realistic values can also help discover bugs. +For this, factory_boy relies on the excellent `fake-factory <https://pypi.python.org/pypi/fake-factory>`_ library: + +.. code-block:: python + + class RandomUserFactory(factory.Factory): + class Meta: + model = models.User + + first_name = factory.Faker('first_name') + last_name = factory.Faker('last_name') + +.. code-block:: pycon + + >>> UserFactory() + <User: Lucy Murray> + + +.. note:: Use of fully randomized data in tests is quickly a problem for reproducing broken builds. + To that purpose, factory_boy provides helpers to handle the random seeds it uses. + + Lazy Attributes """"""""""""""" @@ -123,7 +215,9 @@ These "lazy" attributes can be added as follows: .. code-block:: python class UserFactory(factory.Factory): - FACTORY_FOR = models.User + class Meta: + model = models.User + first_name = 'Joe' last_name = 'Blow' email = factory.LazyAttribute(lambda a: '{0}.{1}@example.com'.format(a.first_name, a.last_name).lower()) @@ -142,7 +236,9 @@ Unique values in a specific format (for example, e-mail addresses) can be genera .. code-block:: python class UserFactory(factory.Factory): - FACTORY_FOR = models.User + class Meta: + model = models.User + email = factory.Sequence(lambda n: 'person{0}@example.com'.format(n)) >>> UserFactory().email @@ -160,7 +256,9 @@ This is handled by the ``SubFactory`` helper: .. code-block:: python class PostFactory(factory.Factory): - FACTORY_FOR = models.Post + class Meta: + model = models.Post + author = factory.SubFactory(UserFactory) @@ -190,7 +288,7 @@ Debugging factory_boy Debugging factory_boy can be rather complex due to the long chains of calls. Detailed logging is available through the ``factory`` logger. -A helper, :meth:`factory.debug()`, is available to ease debugging: +A helper, `factory.debug()`, is available to ease debugging: .. code-block:: python @@ -221,12 +319,12 @@ This will yield messages similar to those (artificial indentation): ORM Support """"""""""" -factory_boy has specific support for a few ORMs, through specific :class:`~factory.Factory` subclasses: +factory_boy has specific support for a few ORMs, through specific ``factory.Factory`` subclasses: -* Django, with :class:`~factory.django.DjangoModelFactory` -* Mogo, with :class:`~factory.mogo.MogoFactory` -* MongoEngine, with :class:`~factory.mongoengine.MongoEngineFactory` -* SQLAlchemy, with :class:`~factory.alchemy.SQLAlchemyModelFactory` +* Django, with ``factory.django.DjangoModelFactory`` +* Mogo, with ``factory.mogo.MogoFactory`` +* MongoEngine, with ``factory.mongoengine.MongoEngineFactory`` +* SQLAlchemy, with ``factory.alchemy.SQLAlchemyModelFactory`` Contributing ------------ @@ -239,20 +337,28 @@ All pull request should pass the test suite, which can be launched simply with: .. code-block:: sh - $ python setup.py test + $ make test -.. note:: - Running test requires the unittest2 (standard in Python 2.7+) and mock libraries. +In order to test coverage, please use: +.. code-block:: sh -In order to test coverage, please use: + $ make coverage + + +To test with a specific framework version, you may use: .. code-block:: sh - $ pip install coverage - $ coverage erase; coverage run --branch setup.py test; coverage report + $ make DJANGO=1.7 test + +Valid options are: + +* ``DJANGO`` for ``Django`` +* ``MONGOENGINE`` for ``mongoengine`` +* ``ALCHEMY`` for ``SQLAlchemy`` Contents, indices and tables |