1factory_boy
2===========
3
4.. image:: https://github.com/FactoryBoy/factory_boy/workflows/Test/badge.svg
5    :target: https://github.com/FactoryBoy/factory_boy/actions?query=workflow%3ATest
6
7.. image:: https://github.com/FactoryBoy/factory_boy/workflows/Check/badge.svg
8    :target: https://github.com/FactoryBoy/factory_boy/actions?query=workflow%3ACheck
9
10.. image:: https://img.shields.io/pypi/v/factory_boy.svg
11    :target: https://factoryboy.readthedocs.io/en/latest/changelog.html
12    :alt: Latest Version
13
14.. image:: https://img.shields.io/pypi/pyversions/factory_boy.svg
15    :target: https://pypi.org/project/factory-boy/
16    :alt: Supported Python versions
17
18.. image:: https://img.shields.io/pypi/wheel/factory_boy.svg
19    :target: https://pypi.org/project/factory-boy/
20    :alt: Wheel status
21
22.. image:: https://img.shields.io/pypi/l/factory_boy.svg
23    :target: https://pypi.org/project/factory-boy/
24    :alt: License
25
26factory_boy is a fixtures replacement based on thoughtbot's `factory_bot <https://github.com/thoughtbot/factory_bot>`_.
27
28As a fixtures replacement tool, it aims to replace static, hard to maintain fixtures
29with easy-to-use factories for complex objects.
30
31Instead of building an exhaustive test setup with every possible combination of corner cases,
32``factory_boy`` allows you to use objects customized for the current test,
33while only declaring the test-specific fields:
34
35.. code-block:: python
36
37    class FooTests(unittest.TestCase):
38
39        def test_with_factory_boy(self):
40            # We need a 200€, paid order, shipping to australia, for a VIP customer
41            order = OrderFactory(
42                amount=200,
43                status='PAID',
44                customer__is_vip=True,
45                address__country='AU',
46            )
47            # Run the tests here
48
49        def test_without_factory_boy(self):
50            address = Address(
51                street="42 fubar street",
52                zipcode="42Z42",
53                city="Sydney",
54                country="AU",
55            )
56            customer = Customer(
57                first_name="John",
58                last_name="Doe",
59                phone="+1234",
60                email="john.doe@example.org",
61                active=True,
62                is_vip=True,
63                address=address,
64            )
65            # etc.
66
67factory_boy is designed to work well with various ORMs (Django, MongoDB, SQLAlchemy),
68and can easily be extended for other libraries.
69
70Its main features include:
71
72- Straightforward declarative syntax
73- Chaining factory calls while retaining the global context
74- Support for multiple build strategies (saved/unsaved instances, stubbed objects)
75- Multiple factories per class support, including inheritance
76
77
78Links
79-----
80
81* Documentation: https://factoryboy.readthedocs.io/
82* Repository: https://github.com/FactoryBoy/factory_boy
83* Package: https://pypi.org/project/factory-boy/
84* Mailing-list: `factoryboy@googlegroups.com <mailto:factoryboy@googlegroups.com>`_ | https://groups.google.com/forum/#!forum/factoryboy
85
86
87Download
88--------
89
90PyPI: https://pypi.org/project/factory-boy/
91
92.. code-block:: sh
93
94    $ pip install factory_boy
95
96Source: https://github.com/FactoryBoy/factory_boy/
97
98.. code-block:: sh
99
100    $ git clone git://github.com/FactoryBoy/factory_boy/
101    $ python setup.py install
102
103
104Usage
105-----
106
107
108.. note:: This section provides a quick summary of factory_boy features.
109          A more detailed listing is available in the full documentation.
110
111
112Defining factories
113""""""""""""""""""
114
115Factories declare a set of attributes used to instantiate a Python object.
116The class of the object must be defined in the ``model`` field of a ``class Meta:`` attribute:
117
118.. code-block:: python
119
120    import factory
121    from . import models
122
123    class UserFactory(factory.Factory):
124        class Meta:
125            model = models.User
126
127        first_name = 'John'
128        last_name = 'Doe'
129        admin = False
130
131    # Another, different, factory for the same object
132    class AdminFactory(factory.Factory):
133        class Meta:
134            model = models.User
135
136        first_name = 'Admin'
137        last_name = 'User'
138        admin = True
139
140
141ORM integration
142"""""""""""""""
143
144factory_boy integration with Object Relational Mapping (ORM) tools is provided
145through specific ``factory.Factory`` subclasses:
146
147* Django, with ``factory.django.DjangoModelFactory``
148* Mogo, with ``factory.mogo.MogoFactory``
149* MongoEngine, with ``factory.mongoengine.MongoEngineFactory``
150* SQLAlchemy, with ``factory.alchemy.SQLAlchemyModelFactory``
151
152More details can be found in the ORM section.
153
154
155Using factories
156"""""""""""""""
157
158factory_boy supports several different build strategies: build, create, and stub:
159
160.. code-block:: python
161
162    # Returns a User instance that's not saved
163    user = UserFactory.build()
164
165    # Returns a saved User instance.
166    # UserFactory must subclass an ORM base class, such as DjangoModelFactory.
167    user = UserFactory.create()
168
169    # Returns a stub object (just a bunch of attributes)
170    obj = UserFactory.stub()
171
172
173You can use the Factory class as a shortcut for the default build strategy:
174
175.. code-block:: python
176
177    # Same as UserFactory.create()
178    user = UserFactory()
179
180
181No matter which strategy is used, it's possible to override the defined attributes by passing keyword arguments:
182
183.. code-block:: pycon
184
185    # Build a User instance and override first_name
186    >>> user = UserFactory.build(first_name='Joe')
187    >>> user.first_name
188    "Joe"
189
190
191It is also possible to create a bunch of objects in a single call:
192
193.. code-block:: pycon
194
195    >>> users = UserFactory.build_batch(10, first_name="Joe")
196    >>> len(users)
197    10
198    >>> [user.first_name for user in users]
199    ["Joe", "Joe", "Joe", "Joe", "Joe", "Joe", "Joe", "Joe", "Joe", "Joe"]
200
201
202Realistic, random values
203""""""""""""""""""""""""
204
205Demos look better with random yet realistic values; and those realistic values can also help discover bugs.
206For this, factory_boy relies on the excellent `faker <https://faker.readthedocs.io/en/latest/>`_ library:
207
208.. code-block:: python
209
210    class RandomUserFactory(factory.Factory):
211        class Meta:
212            model = models.User
213
214        first_name = factory.Faker('first_name')
215        last_name = factory.Faker('last_name')
216
217.. code-block:: pycon
218
219    >>> RandomUserFactory()
220    <User: Lucy Murray>
221
222
223Reproducible random values
224""""""""""""""""""""""""""
225
226The use of fully randomized data in tests is quickly a problem for reproducing broken builds.
227To that purpose, factory_boy provides helpers to handle the random seeds it uses, located in the ``factory.random`` module:
228
229.. code-block:: python
230
231    import factory.random
232
233    def setup_test_environment():
234        factory.random.reseed_random('my_awesome_project')
235        # Other setup here
236
237
238Lazy Attributes
239"""""""""""""""
240
241Most factory attributes can be added using static values that are evaluated when the factory is defined,
242but some attributes (such as fields whose value is computed from other elements)
243will need values assigned each time an instance is generated.
244
245These "lazy" attributes can be added as follows:
246
247.. code-block:: python
248
249    class UserFactory(factory.Factory):
250        class Meta:
251            model = models.User
252
253        first_name = 'Joe'
254        last_name = 'Blow'
255        email = factory.LazyAttribute(lambda a: '{}.{}@example.com'.format(a.first_name, a.last_name).lower())
256        date_joined = factory.LazyFunction(datetime.now)
257
258.. code-block:: pycon
259
260    >>> UserFactory().email
261    "joe.blow@example.com"
262
263
264.. note:: ``LazyAttribute`` calls the function with the object being constructed as an argument, when
265          ``LazyFunction`` does not send any argument.
266
267
268Sequences
269"""""""""
270
271Unique values in a specific format (for example, e-mail addresses) can be generated using sequences. Sequences are defined by using ``Sequence`` or the decorator ``sequence``:
272
273.. code-block:: python
274
275    class UserFactory(factory.Factory):
276        class Meta:
277            model = models.User
278
279        email = factory.Sequence(lambda n: 'person{}@example.com'.format(n))
280
281    >>> UserFactory().email
282    'person0@example.com'
283    >>> UserFactory().email
284    'person1@example.com'
285
286
287Associations
288""""""""""""
289
290Some objects have a complex field, that should itself be defined from a dedicated factories.
291This is handled by the ``SubFactory`` helper:
292
293.. code-block:: python
294
295    class PostFactory(factory.Factory):
296        class Meta:
297            model = models.Post
298
299        author = factory.SubFactory(UserFactory)
300
301
302The associated object's strategy will be used:
303
304
305.. code-block:: python
306
307    # Builds and saves a User and a Post
308    >>> post = PostFactory()
309    >>> post.id is None  # Post has been 'saved'
310    False
311    >>> post.author.id is None  # post.author has been saved
312    False
313
314    # Builds but does not save a User, and then builds but does not save a Post
315    >>> post = PostFactory.build()
316    >>> post.id is None
317    True
318    >>> post.author.id is None
319    True
320
321Support Policy
322--------------
323
324``factory_boy`` supports active Python versions as well as PyPy3.
325
326- **Python**'s `supported versions
327  <https://devguide.python.org/#status-of-python-branches>`__.
328- **Django**'s `supported
329  versions <https://www.djangoproject.com/download/#supported-versions>`__.
330- **SQLAlchemy**: `latest version on PyPI <https://pypi.org/project/SQLAlchemy/>`__.
331- **MongoEngine**: `latest version on PyPI <https://pypi.org/project/mongoengine/>`__.
332
333Debugging factory_boy
334---------------------
335
336Debugging factory_boy can be rather complex due to the long chains of calls.
337Detailed logging is available through the ``factory`` logger.
338
339A helper, `factory.debug()`, is available to ease debugging:
340
341.. code-block:: python
342
343    with factory.debug():
344        obj = TestModel2Factory()
345
346
347    import logging
348    logger = logging.getLogger('factory')
349    logger.addHandler(logging.StreamHandler())
350    logger.setLevel(logging.DEBUG)
351
352This will yield messages similar to those (artificial indentation):
353
354.. code-block:: ini
355
356    BaseFactory: Preparing tests.test_using.TestModel2Factory(extra={})
357      LazyStub: Computing values for tests.test_using.TestModel2Factory(two=<OrderedDeclarationWrapper for <factory.declarations.SubFactory object at 0x1e15610>>)
358        SubFactory: Instantiating tests.test_using.TestModelFactory(__containers=(<LazyStub for tests.test_using.TestModel2Factory>,), one=4), create=True
359        BaseFactory: Preparing tests.test_using.TestModelFactory(extra={'__containers': (<LazyStub for tests.test_using.TestModel2Factory>,), 'one': 4})
360          LazyStub: Computing values for tests.test_using.TestModelFactory(one=4)
361          LazyStub: Computed values, got tests.test_using.TestModelFactory(one=4)
362        BaseFactory: Generating tests.test_using.TestModelFactory(one=4)
363      LazyStub: Computed values, got tests.test_using.TestModel2Factory(two=<tests.test_using.TestModel object at 0x1e15410>)
364    BaseFactory: Generating tests.test_using.TestModel2Factory(two=<tests.test_using.TestModel object at 0x1e15410>)
365
366Contributing
367------------
368
369factory_boy is distributed under the MIT License.
370
371Issues should be opened through `GitHub Issues <https://github.com/FactoryBoy/factory_boy/issues/>`_; whenever possible, a pull request should be included.
372Questions and suggestions are welcome on the `mailing-list <mailto:factoryboy@googlegroups.com>`_.
373
374Development dependencies can be installed in a `virtualenv
375<https://docs.python.org/3/tutorial/venv.html>`_ with:
376
377.. code-block:: sh
378
379    $ pip install --editable '.[dev]'
380
381All pull requests should pass the test suite, which can be launched simply with:
382
383.. code-block:: sh
384
385    $ make testall
386
387
388
389In order to test coverage, please use:
390
391.. code-block:: sh
392
393    $ make coverage
394
395
396To test with a specific framework version, you may use a ``tox`` target:
397
398.. code-block:: sh
399
400    # list all tox environments
401    $ tox --listenvs
402
403    # run tests inside a specific environment
404    $ tox -e py36-django20-alchemy13-mongoengine017
405
406Valid options are:
407
408* ``DJANGO`` for ``Django``
409* ``MONGOENGINE`` for ``mongoengine``
410* ``ALCHEMY`` for ``SQLAlchemy``
411
412
413To avoid running ``mongoengine`` tests (e.g no MongoDB server installed), run:
414
415.. code-block:: sh
416
417    $ make SKIP_MONGOENGINE=1 test
418