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