1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281
|
Introduction
============
The purpose of factory_boy is to provide a default way of getting a new instance,
while still being able to override some fields on a per-call basis.
.. note:: This section will drive you through an overview of factory_boy's feature.
New users are advised to spend a few minutes browsing through this list
of useful helpers.
Users looking for quick helpers may take a look at :doc:`recipes`,
while those needing detailed documentation will be interested in the :doc:`reference` section.
Basic usage
-----------
Factories declare a set of attributes used to instantiate an object, whose class is defined in the ``class Meta``'s ``model`` attribute:
- Subclass ``factory.Factory`` (or a more suitable subclass)
- Add a ``class Meta:`` block
- Set its ``model`` attribute to the target class
- Add defaults for keyword args to pass to the associated class' ``__init__`` method
.. code-block:: python
import factory
from . import base
class UserFactory(factory.Factory):
class Meta:
model = base.User
firstname = "John"
lastname = "Doe"
You may now get ``base.User`` instances trivially:
.. code-block:: pycon
>>> john = UserFactory()
<User: John Doe>
It is also possible to override the defined attributes by passing keyword arguments to the factory:
.. code-block:: pycon
>>> jack = UserFactory(firstname="Jack")
<User: Jack Doe>
A given class may be associated to many :class:`~factory.Factory` subclasses:
.. code-block:: python
class EnglishUserFactory(factory.Factory):
class Meta:
model = base.User
firstname = "John"
lastname = "Doe"
lang = 'en'
class FrenchUserFactory(factory.Factory):
class Meta:
model = base.User
firstname = "Jean"
lastname = "Dupont"
lang = 'fr'
.. code-block:: pycon
>>> EnglishUserFactory()
<User: John Doe (en)>
>>> FrenchUserFactory()
<User: Jean Dupont (fr)>
Sequences
---------
When a field has a unique key, each object generated by the factory should have a different value for that field.
This is achieved with the :class:`~factory.Sequence` declaration:
.. code-block:: python
class UserFactory(factory.Factory):
class Meta:
model = models.User
username = factory.Sequence(lambda n: 'user%d' % n)
.. code-block:: pycon
>>> UserFactory()
<User: user1>
>>> UserFactory()
<User: user2>
.. note:: For more complex situations, you may also use the :meth:`~factory.@sequence` decorator (note that ``self`` is not added as first parameter):
.. code-block:: python
class UserFactory(factory.Factory):
class Meta:
model = models.User
@factory.sequence
def username(n):
return 'user%d' % n
LazyAttribute
-------------
Some fields may be deduced from others, for instance the email based on the username.
The :class:`~factory.LazyAttribute` handles such cases: it should receive a function
taking the object being built and returning the value for the field:
.. code-block:: python
class UserFactory(factory.Factory):
class Meta:
model = models.User
username = factory.Sequence(lambda n: 'user%d' % n)
email = factory.LazyAttribute(lambda obj: '%s@example.com' % obj.username)
.. code-block:: pycon
>>> UserFactory()
<User: user1 (user1@example.com)>
>>> # The LazyAttribute handles overridden fields
>>> UserFactory(username='john')
<User: john (john@example.com)>
>>> # They can be directly overridden as well
>>> UserFactory(email='doe@example.com')
<User: user3 (doe@example.com)>
.. note:: As for :class:`~factory.Sequence`, a :meth:`~factory.@lazy_attribute` decorator is available:
.. code-block:: python
class UserFactory(factory.Factory):
class Meta:
model = models.User
username = factory.Sequence(lambda n: 'user%d' % n)
@factory.lazy_attribute
def email(self):
return '%s@example.com' % self.username
Inheritance
-----------
Once a "base" factory has been defined for a given class,
alternate versions can be easily defined through subclassing.
The subclassed :class:`~factory.Factory` will inherit all declarations from its parent,
and update them with its own declarations:
.. code-block:: python
class UserFactory(factory.Factory):
class Meta:
model = base.User
firstname = "John"
lastname = "Doe"
group = 'users'
class AdminFactory(UserFactory):
admin = True
group = 'admins'
.. code-block:: pycon
>>> user = UserFactory()
>>> user
<User: John Doe>
>>> user.group
'users'
>>> admin = AdminFactory()
>>> admin
<User: John Doe (admin)>
>>> admin.group # The AdminFactory field has overridden the base field
'admins'
Any argument of all factories in the chain can easily be overridden:
.. code-block:: pycon
>>> super_admin = AdminFactory(group='superadmins', lastname="Lennon")
>>> super_admin
<User: John Lennon (admin)>
>>> super_admin.group # Overridden at call time
'superadmins'
Non-kwarg arguments
-------------------
Some classes take a few, non-kwarg arguments first.
This is handled by the :data:`~factory.FactoryOptions.inline_args` attribute:
.. code-block:: python
class MyFactory(factory.Factory):
class Meta:
model = MyClass
inline_args = ('x', 'y')
x = 1
y = 2
z = 3
.. code-block:: pycon
>>> MyFactory(y=4)
<MyClass(1, 4, z=3)>
Strategies
----------
All factories support two built-in strategies:
* ``build`` provides a local object
* ``create`` instantiates a local object, and saves it to the database.
.. note:: For 1.X versions, the ``create`` will actually call ``AssociatedClass.objects.create``,
as for a Django model.
Starting from 2.0, :meth:`factory.Factory.create` simply calls ``AssociatedClass(**kwargs)``.
You should use :class:`~factory.django.DjangoModelFactory` for Django models.
When a :class:`~factory.Factory` includes related fields (:class:`~factory.SubFactory`, :class:`~factory.RelatedFactory`),
the parent's strategy will be pushed onto related factories.
Calling a :class:`~factory.Factory` subclass will provide an object through the default strategy:
.. code-block:: python
class MyFactory(factory.Factory):
class Meta:
model = MyClass
.. code-block:: pycon
>>> MyFactory.create()
<MyFactory: X (saved)>
>>> MyFactory.build()
<MyFactory: X (unsaved)>
>>> MyFactory() # equivalent to MyFactory.create()
<MyClass: X (saved)>
The default strategy can be changed by setting the ``class Meta`` :attr:`~factory.FactoryOptions.strategy` attribute.
|