factory_boy¶
factory_boy is a fixtures replacement based on thoughtbot’s factory_girl.
As a fixtures replacement tool, it aims to replace static, hard to maintain fixtures with easy-to-use factories for complex object.
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:
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, Mongo, 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
Links¶
- Documentation: https://factoryboy.readthedocs.io/
- Repository: https://github.com/FactoryBoy/factory_boy
- Package: https://pypi.python.org/pypi/factory_boy/
- Mailing-list: factoryboy@googlegroups.com | https://groups.google.com/forum/#!forum/factoryboy
factory_boy supports Python 2.7, 3.2 to 3.5, as well as PyPy; it requires only the standard Python library.
Download¶
PyPI: https://pypi.python.org/pypi/factory_boy/
$ pip install factory_boy
Source: https://github.com/FactoryBoy/factory_boy/
$ git clone git://github.com/FactoryBoy/factory_boy/
$ python setup.py install
Usage¶
Note
This section provides a quick summary of factory_boy features. A more detailed listing is available in the full documentation.
Defining factories¶
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:
import factory
from . import models
class UserFactory(factory.Factory):
class Meta:
model = models.User
first_name = 'John'
last_name = 'Doe'
admin = False
# Another, different, factory for the same object
class AdminFactory(factory.Factory):
class Meta:
model = models.User
first_name = 'Admin'
last_name = 'User'
admin = True
Using factories¶
factory_boy supports several different build strategies: build, create, and stub:
# Returns a User instance that's not saved
user = UserFactory.build()
# Returns a saved User instance
user = UserFactory.create()
# 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:
# Same as UserFactory.create()
user = UserFactory()
No matter which strategy is used, it’s possible to override the defined attributes by passing keyword arguments:
# Build a User instance and override first_name
>>> user = UserFactory.build(first_name='Joe')
>>> user.first_name
"Joe"
It is also possible to create a bunch of objects in a single call:
>>> 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 library:
class RandomUserFactory(factory.Factory):
class Meta:
model = models.User
first_name = factory.Faker('first_name')
last_name = factory.Faker('last_name')
>>> 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¶
Most factory attributes can be added using static values that are evaluated when the factory is defined, but some attributes (such as fields whose value is computed from other elements) will need values assigned each time an instance is generated.
These “lazy” attributes can be added as follows:
class UserFactory(factory.Factory):
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())
date_joined = factory.LazyFunction(datetime.now)
>>> UserFactory().email
"joe.blow@example.com"
Note
LazyAttribute
calls the function with the object being constructed as an argument, when
LazyFunction
does not send any argument.
Sequences¶
Unique 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
:
class UserFactory(factory.Factory):
class Meta:
model = models.User
email = factory.Sequence(lambda n: 'person{0}@example.com'.format(n))
>>> UserFactory().email
'person0@example.com'
>>> UserFactory().email
'person1@example.com'
Associations¶
Some objects have a complex field, that should itself be defined from a dedicated factories.
This is handled by the SubFactory
helper:
class PostFactory(factory.Factory):
class Meta:
model = models.Post
author = factory.SubFactory(UserFactory)
The associated object’s strategy will be used:
# Builds and saves a User and a Post
>>> post = PostFactory()
>>> post.id is None # Post has been 'saved'
False
>>> post.author.id is None # post.author has been saved
False
# Builds but does not save a User, and then builds but does not save a Post
>>> post = PostFactory.build()
>>> post.id is None
True
>>> post.author.id is None
True
ORM Support¶
factory_boy has specific support for a few ORMs, through specific factory.Factory
subclasses:
- Django, with
factory.django.DjangoModelFactory
- Mogo, with
factory.mogo.MogoFactory
- MongoEngine, with
factory.mongoengine.MongoEngineFactory
- SQLAlchemy, with
factory.alchemy.SQLAlchemyModelFactory
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, factory.debug(), is available to ease debugging:
with factory.debug():
obj = TestModel2Factory()
import logging
logger = logging.getLogger('factory')
logger.addHandler(logging.StreamHandler())
logger.setLevel(logging.DEBUG)
This will yield messages similar to those (artificial indentation):
BaseFactory: Preparing tests.test_using.TestModel2Factory(extra={})
LazyStub: Computing values for tests.test_using.TestModel2Factory(two=<OrderedDeclarationWrapper for <factory.declarations.SubFactory object at 0x1e15610>>)
SubFactory: Instantiating tests.test_using.TestModelFactory(__containers=(<LazyStub for tests.test_using.TestModel2Factory>,), one=4), create=True
BaseFactory: Preparing tests.test_using.TestModelFactory(extra={'__containers': (<LazyStub for tests.test_using.TestModel2Factory>,), 'one': 4})
LazyStub: Computing values for tests.test_using.TestModelFactory(one=4)
LazyStub: Computed values, got tests.test_using.TestModelFactory(one=4)
BaseFactory: Generating tests.test_using.TestModelFactory(one=4)
LazyStub: Computed values, got tests.test_using.TestModel2Factory(two=<tests.test_using.TestModel object at 0x1e15410>)
BaseFactory: Generating tests.test_using.TestModel2Factory(two=<tests.test_using.TestModel object at 0x1e15410>)
Contributing¶
factory_boy is distributed under the MIT License.
Issues should be opened through GitHub Issues; whenever possible, a pull request should be included. Questions and suggestions are welcome on the mailing-list.
All pull request should pass the test suite, which can be launched simply with:
$ make test
In order to test coverage, please use:
$ make coverage
To test with a specific framework version, you may use:
$ make DJANGO=1.9 test
Valid options are:
DJANGO
forDjango
MONGOENGINE
formongoengine
ALCHEMY
forSQLAlchemy
To avoid running mongoengine
tests (e.g no mongo server installed), run:
$ make SKIP_MONGOENGINE=1 test
Contents, indices and tables¶
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 Common recipes, while those needing detailed documentation will be interested in the 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
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:
>>> john = UserFactory()
<User: John Doe>
It is also possible to override the defined attributes by passing keyword arguments to the factory:
>>> jack = UserFactory(firstname="Jack")
<User: Jack Doe>
A given class may be associated to many Factory
subclasses:
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'
>>> 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 Sequence
declaration:
class UserFactory(factory.Factory):
class Meta:
model = models.User
username = factory.Sequence(lambda n: 'user%d' % n)
>>> UserFactory()
<User: user1>
>>> UserFactory()
<User: user2>
Note
For more complex situations, you may also use the @sequence()
decorator (note that self
is not added as first parameter):
class UserFactory(factory.Factory):
class Meta:
model = models.User
@factory.sequence
def username(n):
return 'user%d' % n
LazyFunction¶
In simple cases, calling a function is enough to compute the value. If that function doesn’t depend on the object
being built, use LazyFunction
to call that function; it should receive a function taking no
argument and returning the value for the field:
class LogFactory(factory.Factory):
class Meta:
model = models.Log
timestamp = factory.LazyFunction(datetime.now)
>>> LogFactory()
<Log: log at 2016-02-12 17:02:34>
>>> # The LazyFunction can be overriden
>>> LogFactory(timestamp=now - timedelta(days=1))
<Log: log at 2016-02-11 17:02:34>
Note
For complex cases when you happen to write a specific function,
the @lazy_attribute()
decorator should be more appropriate.
LazyAttribute¶
Some fields may be deduced from others, for instance the email based on the username.
The LazyAttribute
handles such cases: it should receive a function
taking the object being built and returning the value for the field:
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)
>>> 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 Sequence
, a @lazy_attribute()
decorator is available:
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 Factory
will inherit all declarations from its parent,
and update them with its own declarations:
class UserFactory(factory.Factory):
class Meta:
model = base.User
firstname = "John"
lastname = "Doe"
group = 'users'
class AdminFactory(UserFactory):
admin = True
group = 'admins'
>>> 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:
>>> 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 inline_args
attribute:
class MyFactory(factory.Factory):
class Meta:
model = MyClass
inline_args = ('x', 'y')
x = 1
y = 2
z = 3
>>> MyFactory(y=4)
<MyClass(1, 4, z=3)>
Altering a factory’s behaviour: parameters and traits¶
Some classes are better described with a few, simple parameters, that aren’t fields on the actual model.
In that case, use a Params
declaration:
class RentalFactory(factory.Factory):
class Meta:
model = Rental
begin = factory.fuzzy.FuzzyDate(start_date=datetime.date(2000, 1, 1))
end = factory.LazyAttribute(lambda o: o.begin + o.duration)
class Params:
duration = 12
>>> RentalFactory(duration=0)
<Rental: 2012-03-03 -> 2012-03-03>
>>> RentalFactory(duration=10)
<Rental: 2008-12-16 -> 2012-12-26>
When many fields should be updated based on a flag, use Traits
instead:
class OrderFactory(factory.Factory):
status = 'pending'
shipped_by = None
shipped_on = None
class Meta:
model = Order
class Params:
shipped = factory.Trait(
status='shipped',
shipped_by=factory.SubFactory(EmployeeFactory),
shipped_on=factory.LazyFunction(datetime.date.today),
)
A trait is toggled by a single boolean value:
>>> OrderFactory()
<Order: pending>
>>> OrderFactory(shipped=True)
<Order: shipped by John Doe on 2016-04-02>
Strategies¶
All factories support two built-in strategies:
build
provides a local objectcreate
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, factory.Factory.create()
simply calls AssociatedClass(**kwargs)
.
You should use DjangoModelFactory
for Django models.
When a Factory
includes related fields (SubFactory
, RelatedFactory
),
the parent’s strategy will be pushed onto related factories.
Calling a Factory
subclass will provide an object through the default strategy:
class MyFactory(factory.Factory):
class Meta:
model = MyClass
>>> 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
strategy
attribute.
Reference¶
This section offers an in-depth description of factory_boy features.
For internals and customization points, please refer to the Internals section.
The Factory
class¶
Meta options¶
-
class
factory.
FactoryOptions
¶ New in version 2.4.0.
A
Factory
‘s behaviour can be tuned through a few settings.For convenience, they are declared in a single
class Meta
attribute:class MyFactory(factory.Factory): class Meta: model = MyObject abstract = False
-
model
¶ This optional attribute describes the class of objects to generate.
If unset, it will be inherited from parent
Factory
subclasses.New in version 2.4.0.
-
abstract
¶ This attribute indicates that the
Factory
subclass should not be used to generate objects, but instead provides some extra defaults.It will be automatically set to
True
if neither theFactory
subclass nor its parents define themodel
attribute.New in version 2.4.0.
-
inline_args
¶ Some factories require non-keyword arguments to their
__init__()
. They should be listed, in order, in theinline_args
attribute:class UserFactory(factory.Factory): class Meta: model = User inline_args = ('login', 'email') login = 'john' email = factory.LazyAttribute(lambda o: '%s@example.com' % o.login) firstname = "John"
>>> UserFactory() <User: john> >>> User('john', 'john@example.com', firstname="John") # actual call
New in version 2.4.0.
-
exclude
¶ While writing a
Factory
for some object, it may be useful to have general fields helping defining others, but that should not be passed to the model class; for instance, a field named ‘now’ that would hold a reference time used by other objects.Factory fields whose name are listed in
exclude
will be removed from the set of args/kwargs passed to the underlying class; they can be any valid factory_boy declaration:class OrderFactory(factory.Factory): class Meta: model = Order exclude = ('now',) now = factory.LazyFunction(datetime.datetime.utcnow) started_at = factory.LazyAttribute(lambda o: o.now - datetime.timedelta(hours=1)) paid_at = factory.LazyAttribute(lambda o: o.now - datetime.timedelta(minutes=50))
>>> OrderFactory() # The value of 'now' isn't passed to Order() <Order: started 2013-04-01 12:00:00, paid 2013-04-01 12:10:00> >>> # An alternate value may be passed for 'now' >>> OrderFactory(now=datetime.datetime(2013, 4, 1, 10)) <Order: started 2013-04-01 09:00:00, paid 2013-04-01 09:10:00>
New in version 2.4.0.
-
rename
¶ Sometimes, a model expects a field with a name already used by one of
Factory
‘s methods.In this case, the
rename
attributes allows to define renaming rules: the keys of therename
dict are those used in theFactory
declarations, and their values the new name:class ImageFactory(factory.Factory): # The model expects "attributes" form_attributes = ['thumbnail', 'black-and-white'] class Meta: model = Image rename = {'form_attributes': 'attributes'}
-
strategy
¶ Use this attribute to change the strategy used by a
Factory
. The default isCREATE_STRATEGY
.
-
Attributes and methods¶
-
class
factory.
Factory
¶ Class-level attributes:
-
Meta
¶
-
_meta
¶ New in version 2.4.0.
The
FactoryOptions
instance attached to aFactory
class is available as a_meta
attribute.
-
Params
¶ New in version 2.7.0.
The extra parameters attached to a
Factory
are declared through aParams
class. See the “Parameters” section for more information.
-
_options_class
¶ New in version 2.4.0.
If a
Factory
subclass needs to define additional, extra options, it has to provide a customFactoryOptions
subclass.A pointer to that custom class should be provided as
_options_class
so that theFactory
-building metaclass can use it instead.
Base functions:
The
Factory
class provides a few methods for getting objects; the usual way being to simply call the class:>>> UserFactory() # Calls UserFactory.create() >>> UserFactory(login='john') # Calls UserFactory.create(login='john')
Under the hood, factory_boy will define the
Factory
__new__()
method to call the default strategy of theFactory
.A specific strategy for getting instance can be selected by calling the adequate method:
-
classmethod
build
(cls, **kwargs)¶ Provides a new object, using the ‘build’ strategy.
-
classmethod
build_batch
(cls, size, **kwargs)¶ Provides a list of
size
instances from theFactory
, through the ‘build’ strategy.
-
classmethod
create
(cls, **kwargs)¶ Provides a new object, using the ‘create’ strategy.
-
classmethod
create_batch
(cls, size, **kwargs)¶ Provides a list of
size
instances from theFactory
, through the ‘create’ strategy.
-
classmethod
stub
(cls, **kwargs)¶ Provides a new stub
-
classmethod
generate
(cls, strategy, **kwargs)¶ Provide a new instance, with the provided
strategy
.
-
classmethod
generate_batch
(cls, strategy, size, **kwargs)¶ Provides a list of
size
instances using the specified strategy.
-
classmethod
simple_generate
(cls, create, **kwargs)¶ Provide a new instance, either built (
create=False
) or created (create=True
).
-
classmethod
simple_generate_batch
(cls, create, size, **kwargs)¶ Provides a list of
size
instances, either built or created according tocreate
.
Extension points:
A
Factory
subclass may override a couple of class methods to adapt its behaviour:-
classmethod
_adjust_kwargs
(cls, **kwargs)¶ The
_adjust_kwargs()
extension point allows for late fields tuning.It is called once keyword arguments have been resolved and post-generation items removed, but before the
inline_args
extraction phase.class UserFactory(factory.Factory): @classmethod def _adjust_kwargs(cls, **kwargs): # Ensure ``lastname`` is upper-case. kwargs['lastname'] = kwargs['lastname'].upper() return kwargs
-
classmethod
_setup_next_sequence
(cls)¶ This method will compute the first value to use for the sequence counter of this factory.
It is called when the first instance of the factory (or one of its subclasses) is created.
Subclasses may fetch the next free ID from the database, for instance.
-
classmethod
_build
(cls, model_class, *args, **kwargs)¶ This class method is called whenever a new instance needs to be built. It receives the model class (provided to
model
), and the positional and keyword arguments to use for the class once all has been computed.Subclasses may override this for custom APIs.
-
classmethod
_create
(cls, model_class, *args, **kwargs)¶ The
_create()
method is called whenever an instance needs to be created. It receives the same arguments as_build()
.Subclasses may override this for specific persistence backends:
class BaseBackendFactory(factory.Factory): class Meta: abstract = True # Optional def _create(cls, model_class, *args, **kwargs): obj = model_class(*args, **kwargs) obj.save() return obj
-
classmethod
_after_postgeneration
(cls, obj, create, results=None)¶ Parameters: The
_after_postgeneration()
is called once post-generation declarations have been handled.Its arguments allow to handle specifically some post-generation return values, for instance.
Advanced functions:
-
classmethod
reset_sequence
(cls, value=None, force=False)¶ Parameters: Allows to reset the sequence counter for a
Factory
. The new value can be passed in as thevalue
argument:>>> SomeFactory.reset_sequence(4) >>> SomeFactory._next_sequence 4
Since subclasses of a non-
abstract
Factory
share the same sequence counter, special care needs to be taken when resetting the counter of such a subclass.By default,
reset_sequence()
will raise aValueError
when called on a subclassedFactory
subclass. This can be avoided by passing in theforce=True
flag:>>> InheritedFactory.reset_sequence() Traceback (most recent call last): File "factory_boy/tests/test_base.py", line 179, in test_reset_sequence_subclass_parent SubTestObjectFactory.reset_sequence() File "factory_boy/factory/base.py", line 250, in reset_sequence "Cannot reset the sequence of a factory subclass. " ValueError: Cannot reset the sequence of a factory subclass. Please call reset_sequence() on the root factory, or call reset_sequence(forward=True). >>> InheritedFactory.reset_sequence(force=True) >>>
This is equivalent to calling
reset_sequence()
on the base factory in the chain.
-
Parameters¶
New in version 2.7.0.
Some models have many fields that can be summarized by a few parameters; for instance, a train with many cars — each complete with serial number, manufacturer, ...; or an order that can be pending/shipped/received, with a few fields to describe each step.
When building instances of such models, a couple of parameters can be enough to determine
all other fields; this is handled by the Params
section of a Factory
declaration.
Simple parameters¶
Some factories only need little data:
class ConferenceFactory(factory.Factory):
class Meta:
model = Conference
class Params:
duration = 'short' # Or 'long'
start_date = factory.fuzzy.FuzzyDate()
end_date = factory.LazyAttribute(
lambda o: o.start_date + datetime.timedelta(days=2 if o.duration == 'short' else 7)
)
sprints_start = factory.LazyAttribute(
lambda o: o.end_date - datetime.timedelta(days=0 if o.duration == 'short' else 1)
)
>>> Conference(duration='short')
<Conference: DUTH 2015 (2015-11-05 - 2015-11-08, sprints 2015-11-08)>
>>> Conference(duration='long')
<Conference: DjangoConEU 2016 (2016-03-30 - 2016-04-03, sprints 2016-04-02)>
Any simple parameter provided to the Factory.Params
section is available to the whole factory,
but not passed to the final class (similar to the exclude
behavior).
Traits¶
-
class
factory.
Trait
(**kwargs)[source]¶ New in version 2.7.0.
A trait’s parameters are the fields it should alter when enabled.
For more complex situations, it is helpful to override a few fields at once:
class OrderFactory(factory.Factory):
class Meta:
model = Order
state = 'pending'
shipped_on = None
shipped_by = None
class Params:
shipped = factory.Trait(
state='shipped',
shipped_on=datetime.date.today(),
shipped_by=factory.SubFactory(EmployeeFactory),
)
Such a Trait
is activated or disabled by a single boolean field:
>>> OrderFactory()
<Order: pending>
Order(state='pending')
>>> OrderFactory(shipped=True)
<Order: shipped by John Doe on 2016-04-02>
A Trait
can be enabled/disabled by a Factory
subclass:
class ShippedOrderFactory(OrderFactory):
shipped = True
Values set in a Trait
can be overridden by call-time values:
>>> OrderFactory(shipped=True, shipped_on=last_year)
<Order: shipped by John Doe on 2015-04-20>
Traits
can be chained:
class OrderFactory(factory.Factory):
class Meta:
model = Order
# Can be pending/shipping/received
state = 'pending'
shipped_on = None
shipped_by = None
received_on = None
received_by = None
class Params:
shipped = factory.Trait(
state='shipped',
shipped_on=datetime.date.today,
shipped_by=factory.SubFactory(EmployeeFactory),
)
received = factory.Trait(
shipped=True,
state='received',
shipped_on=datetime.date.today - datetime.timedelta(days=4),
received_on=datetime.date.today,
received_by=factory.SubFactory(CustomerFactory),
)
>>> OrderFactory(received=True)
<Order: shipped by John Doe on 2016-03-20, received by Joan Smith on 2016-04-02>
A Trait
might be overridden in Factory
subclasses:
class LocalOrderFactory(OrderFactory):
class Params:
received = factory.Trait(
shipped=True,
state='received',
shipped_on=datetime.date.today - datetime.timedelta(days=1),
received_on=datetime.date.today,
received_by=factory.SubFactory(CustomerFactory),
)
>>> LocalOrderFactory(received=True)
<Order: shipped by John Doe on 2016-04-01, received by Joan Smith on 2016-04-02>
Note
When overriding a Trait
, the whole declaration MUST be replaced.
Strategies¶
factory_boy supports two main strategies for generating instances, plus stubs.
-
factory.
BUILD_STRATEGY
¶ The ‘build’ strategy is used when an instance should be created, but not persisted to any datastore.
It is usually a simple call to the
__init__()
method of themodel
class.
-
factory.
CREATE_STRATEGY
¶ The ‘create’ strategy builds and saves an instance into its appropriate datastore.
This is the default strategy of factory_boy; it would typically instantiate an object, then save it:
>>> obj = self._associated_class(*args, **kwargs) >>> obj.save() >>> return obj
Warning
For backward compatibility reasons, the default behaviour of factory_boy is to call
MyClass.objects.create(*args, **kwargs)
when using thecreate
strategy.That policy will be used if the
associated class
has anobjects
attribute and the_create()
classmethod of theFactory
wasn’t overridden.
-
factory.
use_strategy
(strategy)[source]¶ Decorator
Change the default strategy of the decorated
Factory
to the chosenstrategy
:@use_strategy(factory.BUILD_STRATEGY) class UserBuildingFactory(UserFactory): pass
-
factory.
STUB_STRATEGY
¶ The ‘stub’ strategy is an exception in the factory_boy world: it doesn’t return an instance of the
model
class, and actually doesn’t require one to be present.Instead, it returns an instance of
StubObject
whose attributes have been set according to the declarations.
-
class
factory.
StubObject
(object)¶ A plain, stupid object. No method, no helpers, simply a bunch of attributes.
It is typically instantiated, then has its attributes set:
>>> obj = StubObject() >>> obj.x = 1 >>> obj.y = 2
-
class
factory.
StubFactory
(Factory)[source]¶ An
abstract
Factory
, with a default strategy set toSTUB_STRATEGY
.
-
factory.
debug
(logger='factory', stream=None)[source]¶ Parameters: - logger (str) – The name of the logger to enable debug for
- stream (file) – The stream to send debug output to, defaults to
sys.stderr
Context manager to help debugging factory_boy behavior. It will temporarily put the target logger (e.g
'factory'
) in debug mode, sending all output to :obj`~sys.stderr`; upon leaving the context, the logging levels are reset.A typical use case is to understand what happens during a single factory call:
with factory.debug(): obj = TestModel2Factory()
This will yield messages similar to those (artificial indentation):
BaseFactory: Preparing tests.test_using.TestModel2Factory(extra={}) LazyStub: Computing values for tests.test_using.TestModel2Factory(two=<OrderedDeclarationWrapper for <factory.declarations.SubFactory object at 0x1e15610>>) SubFactory: Instantiating tests.test_using.TestModelFactory(__containers=(<LazyStub for tests.test_using.TestModel2Factory>,), one=4), create=True BaseFactory: Preparing tests.test_using.TestModelFactory(extra={'__containers': (<LazyStub for tests.test_using.TestModel2Factory>,), 'one': 4}) LazyStub: Computing values for tests.test_using.TestModelFactory(one=4) LazyStub: Computed values, got tests.test_using.TestModelFactory(one=4) BaseFactory: Generating tests.test_using.TestModelFactory(one=4) LazyStub: Computed values, got tests.test_using.TestModel2Factory(two=<tests.test_using.TestModel object at 0x1e15410>) BaseFactory: Generating tests.test_using.TestModel2Factory(two=<tests.test_using.TestModel object at 0x1e15410>)
Declarations¶
Faker¶
-
class
factory.
Faker
(provider, locale=None, **kwargs)[source]¶ In order to easily define realistic-looking factories, use the
Faker
attribute declaration.This is a wrapper around fake-factory; its argument is the name of a
fake-factory
provider:class UserFactory(factory.Factory): class Meta: model = User name = factory.Faker('name')
>>> user = UserFactory() >>> user.name 'Lucy Cechtelar'
-
locale
¶ If a custom locale is required for one specific field, use the
locale
parameter:class UserFactory(factory.Factory): class Meta: model = User name = factory.Faker('name', locale='fr_FR')
>>> user = UserFactory() >>> user.name 'Jean Valjean'
-
classmethod
override_default_locale
(cls, locale)[source]¶ If the locale needs to be overridden for a whole test, use
override_default_locale()
:>>> with factory.Faker.override_default_locale('de_DE'): ... UserFactory() <User: Johannes Brahms>
-
classmethod
add_provider
(cls, locale=None)[source]¶ Some projects may need to fake fields beyond those provided by
fake-factory
; in such cases, usefactory.Faker.add_provider()
to declare additional providers for those fields:factory.Faker.add_provider(SmileyProvider) class FaceFactory(factory.Factory): class Meta: model = Face smiley = factory.Faker('smiley')
-
LazyFunction¶
The LazyFunction
is the simplest case where the value of an attribute
does not depend on the object being built.
It takes as argument a method to call (function, lambda...); that method should not take any argument, though keyword arguments are safe but unused, and return a value.
class LogFactory(factory.Factory):
class Meta:
model = models.Log
timestamp = factory.LazyFunction(datetime.now)
>>> LogFactory()
<Log: log at 2016-02-12 17:02:34>
>>> # The LazyFunction can be overriden
>>> LogFactory(timestamp=now - timedelta(days=1))
<Log: log at 2016-02-11 17:02:34>
Decorator¶
The class LazyFunction
does not provide a decorator.
For complex cases, use LazyAttribute.lazy_attribute()
directly.
LazyAttribute¶
The LazyAttribute
is a simple yet extremely powerful building brick
for extending a Factory
.
It takes as argument a method to call (usually a lambda); that method should accept the object being built as sole argument, and return a value.
class UserFactory(factory.Factory):
class Meta:
model = User
username = 'john'
email = factory.LazyAttribute(lambda o: '%s@example.com' % o.username)
>>> u = UserFactory()
>>> u.email
'john@example.com'
>>> u = UserFactory(username='leo')
>>> u.email
'leo@example.com'
The object passed to LazyAttribute
is not an instance of the target class,
but instead a LazyStub
: a temporary container that computes
the value of all declared fields.
Decorator¶
If a simple lambda isn’t enough, you may use the lazy_attribute()
decorator instead.
This decorates an instance method that should take a single argument, self
;
the name of the method will be used as the name of the attribute to fill with the
return value of the method:
class UserFactory(factory.Factory)
class Meta:
model = User
name = u"Jean"
@factory.lazy_attribute
def email(self):
# Convert to plain ascii text
clean_name = (unicodedata.normalize('NFKD', self.name)
.encode('ascii', 'ignore')
.decode('utf8'))
return u'%s@example.com' % clean_name
>>> joel = UserFactory(name=u"Joël")
>>> joel.email
u'joel@example.com'
Sequence¶
If a field should be unique, and thus different for all built instances,
use a Sequence
.
This declaration takes a single argument, a function accepting a single parameter - the current sequence counter - and returning the related value.
Note
An extra kwarg argument, type
, may be provided.
This feature was deprecated in 1.3.0 and will be removed in 2.0.0.
class UserFactory(factory.Factory)
class Meta:
model = User
phone = factory.Sequence(lambda n: '123-555-%04d' % n)
>>> UserFactory().phone
'123-555-0001'
>>> UserFactory().phone
'123-555-0002'
Decorator¶
As with lazy_attribute()
, a decorator is available for complex situations.
sequence()
decorates an instance method, whose self
method will actually
be the sequence counter - this might be confusing:
class UserFactory(factory.Factory)
class Meta:
model = User
@factory.sequence
def phone(n):
a = n // 10000
b = n % 10000
return '%03d-555-%04d' % (a, b)
>>> UserFactory().phone
'000-555-9999'
>>> UserFactory().phone
'001-555-0000'
Sharing¶
The sequence counter is shared across all Sequence
attributes of the
Factory
:
class UserFactory(factory.Factory):
class Meta:
model = User
phone = factory.Sequence(lambda n: '%04d' % n)
office = factory.Sequence(lambda n: 'A23-B%03d' % n)
>>> u = UserFactory()
>>> u.phone, u.office
'0041', 'A23-B041'
>>> u2 = UserFactory()
>>> u2.phone, u2.office
'0042', 'A23-B042'
Inheritance¶
When a Factory
inherits from another Factory
and the model
of the subclass inherits from the model of the parent, the sequence counter
is shared across the Factory
classes:
class UserFactory(factory.Factory):
class Meta:
model = User
phone = factory.Sequence(lambda n: '123-555-%04d' % n)
class EmployeeFactory(UserFactory):
office_phone = factory.Sequence(lambda n: '%04d' % n)
>>> u = UserFactory()
>>> u.phone
'123-555-0001'
>>> e = EmployeeFactory()
>>> e.phone, e.office_phone
'123-555-0002', '0002'
>>> u2 = UserFactory()
>>> u2.phone
'123-555-0003'
Forcing a sequence counter¶
If a specific value of the sequence counter is required for one instance, the
__sequence
keyword argument should be passed to the factory method.
This will force the sequence counter during the call, without altering the class-level value.
class UserFactory(factory.Factory):
class Meta:
model = User
uid = factory.Sequence(int)
>>> UserFactory()
<User: 0>
>>> UserFactory()
<User: 1>
>>> UserFactory(__sequence=42)
<User: 42>
Warning
The impact of setting __sequence=n
on a _batch
call is
undefined. Each generated instance may share a same counter, or
use incremental values starting from the forced value.
LazyAttributeSequence¶
The LazyAttributeSequence
declaration merges features of Sequence
and LazyAttribute
.
It takes a single argument, a function whose two parameters are, in order:
- The object being built
- The sequence counter
class UserFactory(factory.Factory):
class Meta:
model = User
login = 'john'
email = factory.LazyAttributeSequence(lambda o, n: '%s@s%d.example.com' % (o.login, n))
>>> UserFactory().email
'john@s1.example.com'
>>> UserFactory(login='jack').email
'jack@s2.example.com'
Decorator¶
As for lazy_attribute()
and sequence()
, the lazy_attribute_sequence()
handles more complex cases:
class UserFactory(factory.Factory):
class Meta:
model = User
login = 'john'
@lazy_attribute_sequence
def email(self, n):
bucket = n % 10
return '%s@s%d.example.com' % (self.login, bucket)
SubFactory¶
This attribute declaration calls another Factory
subclass,
selecting the same build strategy and collecting extra kwargs in the process.
The SubFactory
attribute should be called with:
- A
Factory
subclass as first argument, or the fully qualified import path to thatFactory
(see Circular imports) - An optional set of keyword arguments that should be passed when calling that factory
Note
When passing an actual Factory
for the
factory
argument, make sure to pass
the class and not instance (i.e no ()
after the class):
class FooFactory(factory.Factory):
class Meta:
model = Foo
bar = factory.SubFactory(BarFactory) # Not BarFactory()
Definition¶
# A standard factory
class UserFactory(factory.Factory):
class Meta:
model = User
# Various fields
first_name = 'John'
last_name = factory.Sequence(lambda n: 'D%se' % ('o' * n)) # De, Doe, Dooe, Doooe, ...
email = factory.LazyAttribute(lambda o: '%s.%s@example.org' % (o.first_name.lower(), o.last_name.lower()))
# A factory for an object with a 'User' field
class CompanyFactory(factory.Factory):
class Meta:
model = Company
name = factory.Sequence(lambda n: 'FactoryBoyz' + 'z' * n)
# Let's use our UserFactory to create that user, and override its first name.
owner = factory.SubFactory(UserFactory, first_name='Jack')
Calling¶
The wrapping factory will call of the inner factory:
>>> c = CompanyFactory()
>>> c
<Company: FactoryBoyz>
# Notice that the first_name was overridden
>>> c.owner
<User: Jack De>
>>> c.owner.email
jack.de@example.org
Fields of the SubFactory
may be overridden from the external factory:
>>> c = CompanyFactory(owner__first_name='Henry')
>>> c.owner
<User: Henry Doe>
# Notice that the updated first_name was propagated to the email LazyAttribute.
>>> c.owner.email
henry.doe@example.org
# It is also possible to override other fields of the SubFactory
>>> c = CompanyFactory(owner__last_name='Jones')
>>> c.owner
<User: Henry Jones>
>>> c.owner.email
henry.jones@example.org
Strategies¶
The strategy chosen for the external factory will be propagated to all subfactories:
>>> c = CompanyFactory()
>>> c.pk # Saved to the database
3
>>> c.owner.pk # Saved to the database
8
>>> c = CompanyFactory.build()
>>> c.pk # Not saved
None
>>> c.owner.pk # Not saved either
None
Circular imports¶
Some factories may rely on each other in a circular manner.
This issue can be handled by passing the absolute import path to the target
Factory
to the SubFactory
.
New in version 1.3.0.
class UserFactory(factory.Factory):
class Meta:
model = User
username = 'john'
main_group = factory.SubFactory('users.factories.GroupFactory')
class GroupFactory(factory.Factory):
class Meta:
model = Group
name = "MyGroup"
owner = factory.SubFactory(UserFactory)
Obviously, such circular relationships require careful handling of loops:
>>> owner = UserFactory(main_group=None)
>>> UserFactory(main_group__owner=owner)
<john (group: MyGroup)>
SelfAttribute¶
Some fields should reference another field of the object being constructed, or an attribute thereof.
This is performed by the SelfAttribute
declaration.
That declaration takes a single argument, a dot-delimited path to the attribute to fetch:
class UserFactory(factory.Factory)
class Meta:
model = User
birthdate = factory.Sequence(lambda n: datetime.date(2000, 1, 1) + datetime.timedelta(days=n))
birthmonth = factory.SelfAttribute('birthdate.month')
>>> u = UserFactory()
>>> u.birthdate
date(2000, 3, 15)
>>> u.birthmonth
3
Parents¶
When used in conjunction with SubFactory
, the SelfAttribute
gains an “upward” semantic through the double-dot notation, as used in Python imports.
factory.SelfAttribute('..country.language')
means
“Select the language
of the country
of the Factory
calling me”.
class UserFactory(factory.Factory):
class Meta:
model = User
language = 'en'
class CompanyFactory(factory.Factory):
class Meta:
model = Company
country = factory.SubFactory(CountryFactory)
owner = factory.SubFactory(UserFactory, language=factory.SelfAttribute('..country.language'))
>>> company = CompanyFactory()
>>> company.country.language
'fr'
>>> company.owner.language
'fr'
Obviously, this “follow parents” ability also handles overriding some attributes on call:
>>> company = CompanyFactory(country=china)
>>> company.owner.language
'cn'
This feature is also available to LazyAttribute
and LazyAttributeSequence
,
through the factory_parent
attribute of the passed-in object:
class CompanyFactory(factory.Factory):
class Meta:
model = Company
country = factory.SubFactory(CountryFactory)
owner = factory.SubFactory(UserFactory,
language=factory.LazyAttribute(lambda user: user.factory_parent.country.language),
)
Iterator¶
-
class
factory.
Iterator
(iterable, cycle=True, getter=None)[source]¶ The
Iterator
declaration takes succesive values from the given iterable. When it is exhausted, it starts again from zero (unlesscycle=False
).-
cycle
¶ The
cycle
argument is only useful for advanced cases, where the provided iterable has no end (as wishing to cycle it means storing values in memory...).New in version 1.3.0: The
cycle
argument is available as of v1.3.0; previous versions had a behaviour equivalent tocycle=False
.
-
Each call to the factory will receive the next value from the iterable:
class UserFactory(factory.Factory)
lang = factory.Iterator(['en', 'fr', 'es', 'it', 'de'])
>>> UserFactory().lang
'en'
>>> UserFactory().lang
'fr'
When a value is passed in for the argument, the iterator will not be advanced:
>>> UserFactory().lang
'en'
>>> UserFactory(lang='cn').lang
'cn'
>>> UserFactory().lang
'fr'
Getter¶
Some situations may reuse an existing iterable, using only some component.
This is handled by the getter
attribute: this is a function
that accepts as sole parameter a value from the iterable, and returns an
adequate value.
class UserFactory(factory.Factory):
class Meta:
model = User
# CATEGORY_CHOICES is a list of (key, title) tuples
category = factory.Iterator(User.CATEGORY_CHOICES, getter=lambda c: c[0])
Decorator¶
When generating items of the iterator gets too complex for a simple list comprehension,
use the iterator()
decorator:
Warning
The decorated function takes no argument,
notably no self
parameter.
class UserFactory(factory.Factory):
class Meta:
model = User
@factory.iterator
def name():
with open('test/data/names.dat', 'r') as f:
for line in f:
yield line
Dict and List¶
When a factory expects lists or dicts as arguments, such values can be generated
through the whole range of factory_boy declarations,
with the Dict
and List
attributes:
-
class
factory.
Dict
(params[, dict_factory=factory.DictFactory])[source]¶ The
Dict
class is used for dict-like attributes. It receives as non-keyword argument a dictionary of fields to define, whose value may be any factory-enabled declarations:class UserFactory(factory.Factory): class Meta: model = User is_superuser = False roles = factory.Dict({ 'role1': True, 'role2': False, 'role3': factory.Iterator([True, False]), 'admin': factory.SelfAttribute('..is_superuser'), })
Note
Declarations used as a
Dict
values are evaluated within thatDict
‘s context; this means that you must use the..foo
syntax to access fields defined at the factory level.On the other hand, the
Sequence
counter is aligned on the containing factory’s one.The
Dict
behaviour can be tuned through the following parameters:-
dict_factory
¶ The actual factory to use for generating the dict can be set as a keyword argument, if an exotic dictionary-like object (SortedDict, ...) is required.
-
-
class
factory.
List
(items[, list_factory=factory.ListFactory])[source]¶ The
List
can be used for list-like attributes.Internally, the fields are converted into a
index=value
dict, which makes it possible to override some values at use time:class UserFactory(factory.Factory): class Meta: model = User flags = factory.List([ 'user', 'active', 'admin', ])
>>> u = UserFactory(flags__2='superadmin') >>> u.flags ['user', 'active', 'superadmin']
The
List
behaviour can be tuned through the following parameters:-
list_factory
¶ The actual factory to use for generating the list can be set as a keyword argument, if another type (tuple, set, ...) is required.
-
Post-generation hooks¶
Some objects expect additional method calls or complex processing for proper definition.
For instance, a User
may need to have a related Profile
, where the Profile
is built from the User
object.
- To support this pattern, factory_boy provides the following tools:
PostGenerationMethodCall
: allows you to hook a particular attribute to a function callPostGeneration
: this class allows calling a given function with the generated object as argumentpost_generation()
: decorator performing the same functions asPostGeneration
RelatedFactory
: this builds or creates a given factory after building/creating the first Factory.
Post-generation hooks are called in the same order they are declared in the factory class, so that functions can rely on the side effects applied by the previous post-generation hook.
Extracting parameters¶
All post-building hooks share a common base for picking parameters from the
set of attributes passed to the Factory
.
For instance, a PostGeneration
hook is declared as post
:
class SomeFactory(factory.Factory):
class Meta:
model = SomeObject
@post_generation
def post(obj, create, extracted, **kwargs):
obj.set_origin(create)
When calling the factory, some arguments will be extracted for this method:
- If a
post
argument is passed, it will be passed as theextracted
field - Any argument starting with
post__XYZ
will be extracted, itspost__
prefix removed, and added to the kwargs passed to the post-generation hook.
Extracted arguments won’t be passed to the model
class.
Thus, in the following call:
>>> SomeFactory(
post=1,
post_x=2,
post__y=3,
post__z__t=42,
)
The post
hook will receive 1
as extracted
and {'y': 3, 'z__t': 42}
as keyword arguments; {'post_x': 2}
will be passed to SomeFactory._meta.model
.
PostGeneration¶
The PostGeneration
declaration performs actions once the model object
has been generated.
- Its sole argument is a callable, that will be called once the base object has
- been generated.
Once the base object has been generated, the provided callable will be called
as callable(obj, create, extracted, **kwargs)
, where:
obj
is the base object previously generatedcreate
is a boolean indicating which strategy was usedextracted
isNone
unless a value was passed in for thePostGeneration
declaration atFactory
declaration timekwargs
are any extra parameters passed asattr__key=value
when calling theFactory
:
class UserFactory(factory.Factory):
class Meta:
model = User
login = 'john'
make_mbox = factory.PostGeneration(
lambda obj, create, extracted, **kwargs: os.makedirs(obj.login))
Decorator¶
A decorator is also provided, decorating a single method accepting the same
obj
, created
, extracted
and keyword arguments as PostGeneration
.
class UserFactory(factory.Factory):
class Meta:
model = User
login = 'john'
@factory.post_generation
def mbox(self, create, extracted, **kwargs):
if not create:
return
path = extracted or os.path.join('/tmp/mbox/', self.login)
os.path.makedirs(path)
return path
>>> UserFactory.build() # Nothing was created
>>> UserFactory.create() # Creates dir /tmp/mbox/john
>>> UserFactory.create(login='jack') # Creates dir /tmp/mbox/jack
>>> UserFactory.create(mbox='/tmp/alt') # Creates dir /tmp/alt
PostGenerationMethodCall¶
-
class
factory.
PostGenerationMethodCall
(method_name, *args, **kwargs)[source]¶ The
PostGenerationMethodCall
declaration will call a method on the generated object just after instantiation. This declaration class provides a friendly means of generating attributes of a factory instance during initialization. The declaration is created using the following arguments:-
args
¶ The default set of unnamed arguments to pass to the method given in
method_name
-
kwargs
¶ The default set of keyword arguments to pass to the method given in
method_name
-
Once the factory instance has been generated, the method specified in
method_name
will be called on the generated object
with any arguments specified in the PostGenerationMethodCall
declaration, by
default.
For example, to set a default password on a generated User instance
during instantiation, we could make a declaration for a password
attribute like below:
class UserFactory(factory.Factory):
class Meta:
model = User
username = 'user'
password = factory.PostGenerationMethodCall('set_password',
'defaultpassword')
When we instantiate a user from the UserFactory
, the factory
will create a password attribute by calling User.set_password('defaultpassword')
.
Thus, by default, our users will have a password set to 'defaultpassword'
.
>>> u = UserFactory() # Calls user.set_password('defaultpassword')
>>> u.check_password('defaultpassword')
True
If the PostGenerationMethodCall
declaration contained no
arguments or one argument, an overriding value can be passed
directly to the method through a keyword argument matching the attribute name.
For example we can override the default password specified in the declaration
above by simply passing in the desired password as a keyword argument to the
factory during instantiation.
>>> other_u = UserFactory(password='different') # Calls user.set_password('different')
>>> other_u.check_password('defaultpassword')
False
>>> other_u.check_password('different')
True
Note
For Django models, unless the object method called by
PostGenerationMethodCall
saves the object back to the
database, we will have to explicitly remember to save the object back
if we performed a create()
.
>>> u = UserFactory.create() # u.password has not been saved back to the database
>>> u.save() # we must remember to do it ourselves
We can avoid this by subclassing from DjangoModelFactory
,
instead, e.g.,
class UserFactory(factory.django.DjangoModelFactory):
class Meta:
model = User
username = 'user'
password = factory.PostGenerationMethodCall('set_password',
'defaultpassword')
If instead the PostGenerationMethodCall
declaration uses two or
more positional arguments, the overriding value must be an iterable. For
example, if we declared the password
attribute like the following,
class UserFactory(factory.Factory):
class Meta:
model = User
username = 'user'
password = factory.PostGenerationMethodCall('set_password', '', 'sha1')
then we must be cautious to pass in an iterable for the password
keyword argument when creating an instance from the factory:
>>> UserFactory() # Calls user.set_password('', 'sha1')
>>> UserFactory(password=('test', 'md5')) # Calls user.set_password('test', 'md5')
>>> # Always pass in a good iterable:
>>> UserFactory(password=('test',)) # Calls user.set_password('test')
>>> UserFactory(password='test') # Calls user.set_password('t', 'e', 's', 't')
Note
While this setup provides sane and intuitive defaults for most users, it prevents passing more than one argument when the declaration used zero or one.
In such cases, users are advised to either resort to the more powerful
PostGeneration
or to add the second expected argument default
value to the PostGenerationMethodCall
declaration
(PostGenerationMethodCall('method', 'x', 'y_that_is_the_default')
)
Keywords extracted from the factory arguments are merged into the
defaults present in the PostGenerationMethodCall
declaration.
>>> UserFactory(password__disabled=True) # Calls user.set_password('', 'sha1', disabled=True)
Module-level functions¶
Beyond the Factory
class and the various Declarations classes
and methods, factory_boy exposes a few module-level functions, mostly useful
for lightweight factory generation.
Lightweight factory declaration¶
-
factory.
make_factory
(klass, **kwargs)[source]¶ The
make_factory()
function takes a class, declarations as keyword arguments, and generates a newFactory
for that class accordingly:UserFactory = make_factory(User, login='john', email=factory.LazyAttribute(lambda u: '%s@example.com' % u.login), ) # This is equivalent to: class UserFactory(factory.Factory): class Meta: model = User login = 'john' email = factory.LazyAttribute(lambda u: '%s@example.com' % u.login)
An alternate base class to
Factory
can be specified in theFACTORY_CLASS
argument:UserFactory = make_factory(models.User, login='john', email=factory.LazyAttribute(lambda u: '%s@example.com' % u.login), FACTORY_CLASS=factory.django.DjangoModelFactory, ) # This is equivalent to: class UserFactory(factory.django.DjangoModelFactory): class Meta: model = models.User login = 'john' email = factory.LazyAttribute(lambda u: '%s@example.com' % u.login)
New in version 2.0.0: The
FACTORY_CLASS
kwarg was added in 2.0.0.
Instance building¶
The factory
module provides a bunch of shortcuts for creating a factory and
extracting instances from them:
-
factory.
build_batch
(klass, size, FACTORY_CLASS=None, **kwargs)[source]¶ Create a factory for
klass
using declarations passed in kwargs; return an instance built from that factory, or a list ofsize
instances (forbuild_batch()
).Parameters:
-
factory.
create_batch
(klass, size, FACTORY_CLASS=None, **kwargs)[source]¶ Create a factory for
klass
using declarations passed in kwargs; return an instance created from that factory, or a list ofsize
instances (forcreate_batch()
).Parameters:
-
factory.
stub_batch
(klass, size, FACTORY_CLASS=None, **kwargs)[source]¶ Create a factory for
klass
using declarations passed in kwargs; return an instance stubbed from that factory, or a list ofsize
instances (forstub_batch()
).Parameters:
-
factory.
generate_batch
(klass, strategy, size, FACTORY_CLASS=None, **kwargs)[source]¶ Create a factory for
klass
using declarations passed in kwargs; return an instance generated from that factory with thestrategy
strategy, or a list ofsize
instances (forgenerate_batch()
).Parameters:
-
factory.
simple_generate_batch
(klass, create, size, FACTORY_CLASS=None, **kwargs)[source]¶ Create a factory for
klass
using declarations passed in kwargs; return an instance generated from that factory according to thecreate
flag, or a list ofsize
instances (forsimple_generate_batch()
).Parameters:
Using factory_boy with ORMs¶
factory_boy provides custom Factory
subclasses for various ORMs,
adding dedicated features.
Django¶
The first versions of factory_boy were designed specifically for Django, but the library has now evolved to be framework-independent.
Most features should thus feel quite familiar to Django users.
The DjangoModelFactory
subclass¶
All factories for a Django Model
should use the
DjangoModelFactory
base class.
-
class
factory.django.
DjangoModelFactory
(factory.Factory)[source]¶ Dedicated class for Django
Model
factories.This class provides the following features:
- The
model
attribute also supports the'app.Model'
syntax create()
usesModel.objects.create()
- When using
RelatedFactory
orPostGeneration
attributes, the base object will besaved
once all post-generation hooks have run.
- The
Note
With Django versions 1.8.0 to 1.8.3, it was no longer possible to call .build()
on a factory if this factory used a SubFactory
pointing
to another model: Django refused to set a ForeignKey
to an unsaved Model
instance.
See https://code.djangoproject.com/ticket/10811 and https://code.djangoproject.com/ticket/25160 for details.
-
class
factory.django.
DjangoOptions
(factory.base.FactoryOptions)[source]¶ The
class Meta
on aDjangoModelFactory
supports extra parameters:-
database
¶ New in version 2.5.0.
All queries to the related model will be routed to the given database. It defaults to
'default'
.
-
django_get_or_create
¶ New in version 2.4.0.
Fields whose name are passed in this list will be used to perform a
Model.objects.get_or_create()
instead of the usualModel.objects.create()
:class UserFactory(factory.django.DjangoModelFactory): class Meta: model = 'myapp.User' # Equivalent to ``model = myapp.models.User`` django_get_or_create = ('username',) username = 'john'
>>> User.objects.all() [] >>> UserFactory() # Creates a new user <User: john> >>> User.objects.all() [<User: john>] >>> UserFactory() # Fetches the existing user <User: john> >>> User.objects.all() # No new user! [<User: john>] >>> UserFactory(username='jack') # Creates another user <User: jack> >>> User.objects.all() [<User: john>, <User: jack>]
-
Extra fields¶
-
class
factory.django.
FileField
[source]¶ Custom declarations for
django.db.models.FileField
-
__init__
(self, from_path='', from_file='', data=b'', filename='example.dat')[source]¶ Parameters: - from_path (str) – Use data from the file located at
from_path
, and keep its filename - from_file (file) – Use the contents of the provided file object; use its filename
if available, unless
filename
is also provided. - from_func (func) – Use function that returns a file object
- data (bytes) – Use the provided bytes as file contents
- filename (str) – The filename for the FileField
- from_path (str) – Use data from the file located at
-
Note
If the value None
was passed for the FileField
field, this will
disable field generation:
class MyFactory(factory.django.DjangoModelFactory):
class Meta:
model = models.MyModel
the_file = factory.django.FileField(filename='the_file.dat')
>>> MyFactory(the_file__data=b'uhuh').the_file.read()
b'uhuh'
>>> MyFactory(the_file=None).the_file
None
-
class
factory.django.
ImageField
[source]¶ Custom declarations for
django.db.models.ImageField
-
__init__
(self, from_path='', from_file='', filename='example.jpg', width=100, height=100, color='green', format='JPEG')¶ Parameters: - from_path (str) – Use data from the file located at
from_path
, and keep its filename - from_file (file) – Use the contents of the provided file object; use its filename if available
- from_func (func) – Use function that returns a file object
- filename (str) – The filename for the ImageField
- width (int) – The width of the generated image (default:
100
) - height (int) – The height of the generated image (default:
100
) - color (str) – The color of the generated image (default:
'green'
) - format (str) – The image format (as supported by PIL) (default:
'JPEG'
)
- from_path (str) – Use data from the file located at
-
Note
If the value None
was passed for the FileField
field, this will
disable field generation:
Note
Just as Django’s django.db.models.ImageField
requires the
Python Imaging Library, this ImageField
requires it too.
class MyFactory(factory.django.DjangoModelFactory):
class Meta:
model = models.MyModel
the_image = factory.django.ImageField(color='blue')
>>> MyFactory(the_image__width=42).the_image.width
42
>>> MyFactory(the_image=None).the_image
None
Disabling signals¶
Signals are often used to plug some custom code into external components code;
for instance to create Profile
objects on-the-fly when a new User
object is saved.
This may interfere with finely tuned factories
, which would
create both using RelatedFactory
.
To work around this problem, use the mute_signals()
decorator/context manager:
-
factory.django.
mute_signals
(signal1, ...)[source]¶ Disable the list of selected signals when calling the factory, and reactivate them upon leaving.
# foo/factories.py
import factory
import factory.django
from . import models
from . import signals
@factory.django.mute_signals(signals.pre_save, signals.post_save)
class FooFactory(factory.django.DjangoModelFactory):
class Meta:
model = models.Foo
# ...
def make_chain():
with factory.django.mute_signals(signals.pre_save, signals.post_save):
# pre_save/post_save won't be called here.
return SomeFactory(), SomeOtherFactory()
Mogo¶
factory_boy supports Mogo-style models, through the MogoFactory
class.
Mogo is a wrapper around the pymongo
library for MongoDB.
MongoEngine¶
factory_boy supports MongoEngine-style models, through the MongoEngineFactory
class.
mongoengine is a wrapper around the pymongo
library for MongoDB.
-
class
factory.mongoengine.
MongoEngineFactory
(factory.Factory)[source]¶ Dedicated class for MongoEngine models.
This class provides the following features:
Note
If the
associated class <factory.FactoryOptions.model
is amongoengine.EmbeddedDocument
, thecreate()
function won’t “save” it, since this wouldn’t make sense.This feature makes it possible to use
SubFactory
to create embedded document.
A minimalist example:
import mongoengine
class Address(mongoengine.EmbeddedDocument):
street = mongoengine.StringField()
class Person(mongoengine.Document):
name = mongoengine.StringField()
address = mongoengine.EmbeddedDocumentField(Address)
import factory
class AddressFactory(factory.mongoengine.MongoEngineFactory):
class Meta:
model = Address
street = factory.Sequence(lambda n: 'street%d' % n)
class PersonFactory(factory.mongoengine.MongoEngineFactory):
class Meta:
model = Person
name = factory.Sequence(lambda n: 'name%d' % n)
address = factory.SubFactory(AddressFactory)
SQLAlchemy¶
Factoy_boy also supports SQLAlchemy models through the SQLAlchemyModelFactory
class.
To work, this class needs an SQLAlchemy session object affected to the Meta.sqlalchemy_session
attribute.
-
class
factory.alchemy.
SQLAlchemyModelFactory
(factory.Factory)[source]¶ Dedicated class for SQLAlchemy models.
This class provides the following features:
-
class
factory.alchemy.
SQLAlchemyOptions
(factory.base.FactoryOptions)[source]¶ In addition to the usual parameters available in
class Meta
, aSQLAlchemyModelFactory
also supports the following settings:-
sqlalchemy_session
¶ SQLAlchemy session to use to communicate with the database when creating an object through this
SQLAlchemyModelFactory
.
-
force_flush
¶ Force a session flush() at the end of
_create()
.
-
A (very) simple example:
from sqlalchemy import Column, Integer, Unicode, create_engine
from sqlalchemy.ext.declarative import declarative_base
from sqlalchemy.orm import scoped_session, sessionmaker
engine = create_engine('sqlite://')
session = scoped_session(sessionmaker(bind=engine))
Base = declarative_base()
class User(Base):
""" A SQLAlchemy simple model class who represents a user """
__tablename__ = 'UserTable'
id = Column(Integer(), primary_key=True)
name = Column(Unicode(20))
Base.metadata.create_all(engine)
import factory
class UserFactory(factory.alchemy.SQLAlchemyModelFactory):
class Meta:
model = User
sqlalchemy_session = session # the SQLAlchemy session object
id = factory.Sequence(lambda n: n)
name = factory.Sequence(lambda n: u'User %d' % n)
>>> session.query(User).all()
[]
>>> UserFactory()
<User: User 1>
>>> session.query(User).all()
[<User: User 1>]
Managing sessions¶
Since SQLAlchemy is a general purpose library, there is no “global” session management system.
The most common pattern when working with unit tests and factory_boy
is to use SQLAlchemy‘s sqlalchemy.orm.scoping.scoped_session
:
- The test runner configures some project-wide
scoped_session
- Each
SQLAlchemyModelFactory
subclass uses thisscoped_session
as itssqlalchemy_session
- The
tearDown()
method of tests callsSession.remove
to reset the session.
Note
See the excellent SQLAlchemy guide on scoped_session
for details of scoped_session
‘s usage.
The basic idea is that declarative parts of the code (including factories) need a simple way to access the “current session”, but that session will only be created and configured at a later point.
The scoped_session
handles this,
by virtue of only creating the session when a query is sent to the database.
Here is an example layout:
- A global (test-only?) file holds the
scoped_session
:
# myprojet/test/common.py
from sqlalchemy import orm
Session = orm.scoped_session(orm.sessionmaker())
- All factory access it:
# myproject/factories.py
import factory
import factory.alchemy
from . import models
from .test import common
class UserFactory(factory.alchemy.SQLAlchemyModelFactory):
class Meta:
model = models.User
# Use the not-so-global scoped_session
# Warning: DO NOT USE common.Session()!
sqlalchemy_session = common.Session
name = factory.Sequence(lambda n: "User %d" % n)
- The test runner configures the
scoped_session
when it starts:
# myproject/test/runtests.py
import sqlalchemy
from . import common
def runtests():
engine = sqlalchemy.create_engine('sqlite://')
# It's a scoped_session, and now is the time to configure it.
common.Session.configure(bind=engine)
run_the_tests
test cases
use thisscoped_session
, and clear it after each test (for isolation):
# myproject/test/test_stuff.py
import unittest
from . import common
class MyTest(unittest.TestCase):
def setUp(self):
# Prepare a new, clean session
self.session = common.Session()
def test_something(self):
u = factories.UserFactory()
self.assertEqual([u], self.session.query(User).all())
def tearDown(self):
# Rollback the session => no changes to the database
self.session.rollback()
# Remove it, so that the next test gets a new Session()
common.Session.remove()
Common recipes¶
Note
Most recipes below take on Django model examples, but can also be used on their own.
Dependent objects (ForeignKey)¶
When one attribute is actually a complex field
(e.g a ForeignKey
to another Model
),
use the SubFactory
declaration:
# models.py
class User(models.Model):
first_name = models.CharField()
group = models.ForeignKey(Group)
# factories.py
import factory
from . import models
class UserFactory(factory.django.DjangoModelFactory):
class Meta:
model = models.User
first_name = factory.Sequence(lambda n: "Agent %03d" % n)
group = factory.SubFactory(GroupFactory)
Choosing from a populated table¶
If the target of the ForeignKey
should be
chosen from a pre-populated table
(e.g django.contrib.contenttypes.models.ContentType
),
simply use a factory.Iterator
on the chosen queryset:
import factory, factory.django
from . import models
class UserFactory(factory.django.DjangoModelFactory):
class Meta:
model = models.User
language = factory.Iterator(models.Language.objects.all())
Here, models.Language.objects.all()
won’t be evaluated until the
first call to UserFactory
; thus avoiding DB queries at import time.
Reverse dependencies (reverse ForeignKey)¶
When a related object should be created upon object creation
(e.g a reverse ForeignKey
from another Model
),
use a RelatedFactory
declaration:
# models.py
class User(models.Model):
pass
class UserLog(models.Model):
user = models.ForeignKey(User)
action = models.CharField()
# factories.py
class UserFactory(factory.django.DjangoModelFactory):
class Meta:
model = models.User
log = factory.RelatedFactory(UserLogFactory, 'user', action=models.UserLog.ACTION_CREATE)
When a UserFactory
is instantiated, factory_boy will call
UserLogFactory(user=that_user, action=...)
just before returning the created User
.
Example: Django’s Profile¶
Django (<1.5) provided a mechanism to attach a Profile
to a User
instance,
using a OneToOneField
from the Profile
to the User
.
A typical way to create those profiles was to hook a post-save signal to the User
model.
factory_boy allows to define attributes of such profiles dynamically when creating a User
:
class ProfileFactory(factory.django.DjangoModelFactory):
class Meta:
model = my_models.Profile
title = 'Dr'
# We pass in profile=None to prevent UserFactory from creating another profile
# (this disables the RelatedFactory)
user = factory.SubFactory('app.factories.UserFactory', profile=None)
class UserFactory(factory.django.DjangoModelFactory):
class Meta:
model = auth_models.User
username = factory.Sequence(lambda n: "user_%d" % n)
# We pass in 'user' to link the generated Profile to our just-generated User
# This will call ProfileFactory(user=our_new_user), thus skipping the SubFactory.
profile = factory.RelatedFactory(ProfileFactory, 'user')
@classmethod
def _generate(cls, create, attrs):
"""Override the default _generate() to disable the post-save signal."""
# Note: If the signal was defined with a dispatch_uid, include that in both calls.
post_save.disconnect(handler_create_user_profile, auth_models.User)
user = super(UserFactory, cls)._generate(create, attrs)
post_save.connect(handler_create_user_profile, auth_models.User)
return user
>>> u = UserFactory(profile__title=u"Lord")
>>> u.get_profile().title
u"Lord"
Such behaviour can be extended to other situations where a signal interferes with factory_boy related factories.
Note
When any RelatedFactory
or post_generation
attribute is defined on the DjangoModelFactory
subclass,
a second save()
is performed after the call to _create()
.
Code working with signals should thus override the _generate()
method.
Simple Many-to-many relationship¶
Building the adequate link between two models depends heavily on the use case;
factory_boy doesn’t provide a “all in one tools” as for SubFactory
or RelatedFactory
, users will have to craft their own depending
on the model.
The base building block for this feature is the post_generation
hook:
# models.py
class Group(models.Model):
name = models.CharField()
class User(models.Model):
name = models.CharField()
groups = models.ManyToManyField(Group)
# factories.py
class GroupFactory(factory.django.DjangoModelFactory):
class Meta:
model = models.Group
name = factory.Sequence(lambda n: "Group #%s" % n)
class UserFactory(factory.django.DjangoModelFactory):
class Meta:
model = models.User
name = "John Doe"
@factory.post_generation
def groups(self, create, extracted, **kwargs):
if not create:
# Simple build, do nothing.
return
if extracted:
# A list of groups were passed in, use them
for group in extracted:
self.groups.add(group)
When calling UserFactory()
or UserFactory.build()
, no group binding
will be created.
But when UserFactory.create(groups=(group1, group2, group3))
is called,
the groups
declaration will add passed in groups to the set of groups for the
user.
Many-to-many relation with a ‘through’¶
If only one link is required, this can be simply performed with a RelatedFactory
.
If more links are needed, simply add more RelatedFactory
declarations:
# models.py
class User(models.Model):
name = models.CharField()
class Group(models.Model):
name = models.CharField()
members = models.ManyToManyField(User, through='GroupLevel')
class GroupLevel(models.Model):
user = models.ForeignKey(User)
group = models.ForeignKey(Group)
rank = models.IntegerField()
# factories.py
class UserFactory(factory.django.DjangoModelFactory):
class Meta:
model = models.User
name = "John Doe"
class GroupFactory(factory.django.DjangoModelFactory):
class Meta:
model = models.Group
name = "Admins"
class GroupLevelFactory(factory.django.DjangoModelFactory):
class Meta:
model = models.GroupLevel
user = factory.SubFactory(UserFactory)
group = factory.SubFactory(GroupFactory)
rank = 1
class UserWithGroupFactory(UserFactory):
membership = factory.RelatedFactory(GroupLevelFactory, 'user')
class UserWith2GroupsFactory(UserFactory):
membership1 = factory.RelatedFactory(GroupLevelFactory, 'user', group__name='Group1')
membership2 = factory.RelatedFactory(GroupLevelFactory, 'user', group__name='Group2')
Whenever the UserWithGroupFactory
is called, it will, as a post-generation hook,
call the GroupLevelFactory
, passing the generated user as a user
field:
UserWithGroupFactory()
generates aUser
instance,obj
- It calls
GroupLevelFactory(user=obj)
- It returns
obj
When using the UserWith2GroupsFactory
, that behavior becomes:
UserWith2GroupsFactory()
generates aUser
instance,obj
- It calls
GroupLevelFactory(user=obj, group__name='Group1')
- It calls
GroupLevelFactory(user=obj, group__name='Group2')
- It returns
obj
Copying fields to a SubFactory¶
When a field of a related class should match one of the container:
# models.py
class Country(models.Model):
name = models.CharField()
lang = models.CharField()
class User(models.Model):
name = models.CharField()
lang = models.CharField()
country = models.ForeignKey(Country)
class Company(models.Model):
name = models.CharField()
owner = models.ForeignKey(User)
country = models.ForeignKey(Country)
Here, we want:
- The User to have the lang of its country (
factory.SelfAttribute('country.lang')
) - The Company owner to live in the country of the company (
factory.SelfAttribute('..country')
)
# factories.py
class CountryFactory(factory.django.DjangoModelFactory):
class Meta:
model = models.Country
name = factory.Iterator(["France", "Italy", "Spain"])
lang = factory.Iterator(['fr', 'it', 'es'])
class UserFactory(factory.django.DjangoModelFactory):
class Meta:
model = models.User
name = "John"
lang = factory.SelfAttribute('country.lang')
country = factory.SubFactory(CountryFactory)
class CompanyFactory(factory.django.DjangoModelFactory):
class Meta:
model = models.Company
name = "ACME, Inc."
country = factory.SubFactory(CountryFactory)
owner = factory.SubFactory(UserFactory, country=factory.SelfAttribute('..country'))
If the value of a field on the child factory is indirectly derived from a field on the parent factory, you will need to use LazyAttribute and poke the “factory_parent” attribute.
This time, we want the company owner to live in a country neighboring the country of the company:
class CompanyFactory(factory.django.DjangoModelFactory):
class Meta:
model = models.Company
name = "ACME, Inc."
country = factory.SubFactory(CountryFactory)
owner = factory.SubFactory(UserFactory,
country=factory.LazyAttribute(lambda o: get_random_neighbor(o.factory_parent.country)))
Custom manager methods¶
Sometimes you need a factory to call a specific manager method other then the
default Model.objects.create()
method:
class UserFactory(factory.DjangoModelFactory):
class Meta:
model = UserenaSignup
username = "l7d8s"
email = "my_name@example.com"
password = "my_password"
@classmethod
def _create(cls, model_class, *args, **kwargs):
"""Override the default ``_create`` with our custom call."""
manager = cls._get_manager(model_class)
# The default would use ``manager.create(*args, **kwargs)``
return manager.create_user(*args, **kwargs)
Forcing the sequence counter¶
A common pattern with factory_boy is to use a factory.Sequence
declaration
to provide varying values to attributes declared as unique.
However, it is sometimes useful to force a given value to the counter, for instance to ensure that tests are properly reproductible.
factory_boy provides a few hooks for this:
- Forcing the value on a per-call basis
In order to force the counter for a specific
Factory
instantiation, just pass the value in the__sequence=42
parameter:class AccountFactory(factory.Factory): class Meta: model = Account uid = factory.Sequence(lambda n: n) name = "Test"
>>> obj1 = AccountFactory(name="John Doe", __sequence=10) >>> obj1.uid # Taken from the __sequence counter 10 >>> obj2 = AccountFactory(name="Jane Doe") >>> obj2.uid # The base sequence counter hasn't changed 1
- Resetting the counter globally
If all calls for a factory must start from a deterministic number, use
factory.Factory.reset_sequence()
; this will reset the counter to its initial value (as defined byfactory.Factory._setup_next_sequence()
).>>> AccountFactory().uid 1 >>> AccountFactory().uid 2 >>> AccountFactory.reset_sequence() >>> AccountFactory().uid # Reset to the initial value 1 >>> AccountFactory().uid 2
It is also possible to reset the counter to a specific value:
>>> AccountFactory.reset_sequence(10) >>> AccountFactory().uid 10 >>> AccountFactory().uid 11
- Forcing the initial value for all projects
The sequence counter of a
Factory
can also be set automatically upon the first call through the_setup_next_sequence()
method; this helps when the objects’s attributes mustn’t conflict with pre-existing data.A typical example is to ensure that running a Python script twice will create non-conflicting objects, by setting up the counter to “max used value plus one”:
class AccountFactory(factory.django.DjangoModelFactory): class Meta: model = models.Account @classmethod def _setup_next_sequence(cls): try: return models.Accounts.objects.latest('uid').uid + 1 except models.Account.DoesNotExist: return 1
>>> Account.objects.create(uid=42, name="Blah") >>> AccountFactory.create() # Sets up the account number based on the latest uid <Account uid=43, name=Test>
Converting a factory’s output to a dict¶
In order to inject some data to, say, a REST API, it can be useful to fetch the factory’s data as a dict.
Internally, a factory will:
- Merge declarations and overrides from all sources (class definition, call parameters, ...)
- Resolve them into a dict
- Pass that dict as keyword arguments to the model’s
build
/create
function
In order to get a dict, we’ll just have to swap the model; the easiest way is to use
factory.build()
:
class UserFactory(factory.django.DjangoModelFactory):
class Meta:
model = models.User
first_name = factory.Sequence(lambda n: "Agent %03d" % n)
username = factory.Faker('username')
>>> factory.build(dict, FACTORY_CLASS=UserFactory)
{'first_name': "Agent 001", 'username': 'john_doe'}
Fuzzy attributes¶
Note
Now that FactoryBoy includes the factory.Faker
class, most of
these built-in fuzzers are deprecated in favor of their
Faker equivalents. Further
discussion here:
https://github.com/FactoryBoy/factory_boy/issues/271/
Some tests may be interested in testing with fuzzy, random values.
This is handled by the factory.fuzzy
module, which provides a few
random declarations.
Note
Use import factory.fuzzy
to load this module.
FuzzyAttribute¶
-
class
factory.fuzzy.
FuzzyAttribute
[source]¶ The
FuzzyAttribute
uses an arbitrary callable as fuzzer. It is expected that successive calls of that function return various values.-
fuzzer
¶ The callable that generates random values
-
FuzzyText¶
-
class
factory.fuzzy.
FuzzyText
(length=12, chars=string.ascii_letters, prefix='')[source]¶ The
FuzzyText
fuzzer yields random strings beginning with the givenprefix
, followed bylength
charactes chosen from thechars
character set, and ending with the givensuffix
.-
length
¶ int, the length of the random part
-
prefix
¶ text, an optional prefix to prepend to the random part
-
suffix
¶ text, an optional suffix to append to the random part
-
chars
¶ - char iterable, the chars to choose from; defaults to the list of ascii
- letters and numbers.
-
FuzzyChoice¶
-
class
factory.fuzzy.
FuzzyChoice
(choices)[source]¶ The
FuzzyChoice
fuzzer yields random choices from the given iterable.Note
The passed in
choices
will be converted into a list upon first use, not at declaration time.This allows passing in, for instance, a Django queryset that will only hit the database during the database, not at import time.
-
choices
¶ The list of choices to select randomly
-
FuzzyInteger¶
-
class
factory.fuzzy.
FuzzyInteger
(low[, high[, step]])[source]¶ The
FuzzyInteger
fuzzer generates random integers within a given inclusive range.The
low
bound may be omitted, in which case it defaults to 0:>>> fi = FuzzyInteger(0, 42) >>> fi.low, fi.high 0, 42 >>> fi = FuzzyInteger(42) >>> fi.low, fi.high 0, 42
-
low
¶ int, the inclusive lower bound of generated integers
-
high
¶ int, the inclusive higher bound of generated integers
-
step
¶ int, the step between values in the range; for instance, a
FuzzyInteger(0, 42, step=3)
might only yield values from[0, 3, 6, 9, 12, 15, 18, 21, 24, 27, 30, 33, 36, 39, 42]
.
-
FuzzyDecimal¶
-
class
factory.fuzzy.
FuzzyDecimal
(low[, high[, precision=2]])[source]¶ The
FuzzyDecimal
fuzzer generates randomdecimals
within a given inclusive range.The
low
bound may be omitted, in which case it defaults to 0:>>> FuzzyDecimal(0.5, 42.7) >>> fi.low, fi.high 0.5, 42.7 >>> fi = FuzzyDecimal(42.7) >>> fi.low, fi.high 0.0, 42.7 >>> fi = FuzzyDecimal(0.5, 42.7, 3) >>> fi.low, fi.high, fi.precision 0.5, 42.7, 3
-
low
¶ decimal, the inclusive lower bound of generated decimals
-
high
¶ decimal, the inclusive higher bound of generated decimals
-
precision
¶ -
int, the number of digits to generate after the dot. The default is 2 digits.
-
FuzzyFloat¶
-
class
factory.fuzzy.
FuzzyFloat
(low[, high])[source]¶ The
FuzzyFloat
fuzzer provides randomfloat
objects within a given inclusive range.>>> FuzzyFloat(0.5, 42.7) >>> fi.low, fi.high 0.5, 42.7 >>> fi = FuzzyFloat(42.7) >>> fi.low, fi.high 0.0, 42.7
-
low
¶ decimal, the inclusive lower bound of generated floats
-
high
¶ decimal, the inclusive higher bound of generated floats
-
FuzzyDate¶
-
class
factory.fuzzy.
FuzzyDate
(start_date[, end_date])[source]¶ The
FuzzyDate
fuzzer generates random dates within a given inclusive range.The
end_date
bound may be omitted, in which case it defaults to the current date:>>> fd = FuzzyDate(datetime.date(2008, 1, 1)) >>> fd.start_date, fd.end_date datetime.date(2008, 1, 1), datetime.date(2013, 4, 16)
-
start_date
¶ datetime.date
, the inclusive lower bound of generated dates
-
end_date
¶ datetime.date
, the inclusive higher bound of generated dates
-
FuzzyDateTime¶
-
class
factory.fuzzy.
FuzzyDateTime
(start_dt, [end_dt, ]force_year=None, force_month=None, force_day=None, force_hour=None, force_minute=None, force_second=None, force_microsecond=None)[source]¶ The
FuzzyDateTime
fuzzer generates random timezone-aware datetime within a given inclusive range.The
end_dt
bound may be omitted, in which case it defaults todatetime.datetime.now()
localized into the UTC timezone.>>> fdt = FuzzyDateTime(datetime.datetime(2008, 1, 1, tzinfo=UTC)) >>> fdt.start_dt, fdt.end_dt datetime.datetime(2008, 1, 1, tzinfo=UTC), datetime.datetime(2013, 4, 21, 19, 13, 32, 458487, tzinfo=UTC)
The
force_XXX
keyword arguments force the related value of generated datetimes:>>> fdt = FuzzyDateTime(datetime.datetime(2008, 1, 1, tzinfo=UTC), datetime.datetime(2009, 1, 1, tzinfo=UTC), ... force_day=3, force_second=42) >>> fdt.evaluate(2, None, False) # Actual code used by ``SomeFactory.build()`` datetime.datetime(2008, 5, 3, 12, 13, 42, 124848, tzinfo=UTC)
-
start_dt
¶ datetime.datetime
, the inclusive lower bound of generated datetimes
-
end_dt
¶ datetime.datetime
, the inclusive upper bound of generated datetimes
-
force_microsecond
¶ int or None; if set, forces the
microsecond
of generated datetime.
-
FuzzyNaiveDateTime¶
-
class
factory.fuzzy.
FuzzyNaiveDateTime
(start_dt, [end_dt, ]force_year=None, force_month=None, force_day=None, force_hour=None, force_minute=None, force_second=None, force_microsecond=None)[source]¶ The
FuzzyNaiveDateTime
fuzzer generates random naive datetime within a given inclusive range.The
end_dt
bound may be omitted, in which case it defaults todatetime.datetime.now()
:>>> fdt = FuzzyNaiveDateTime(datetime.datetime(2008, 1, 1)) >>> fdt.start_dt, fdt.end_dt datetime.datetime(2008, 1, 1), datetime.datetime(2013, 4, 21, 19, 13, 32, 458487)
The
force_XXX
keyword arguments force the related value of generated datetimes:>>> fdt = FuzzyNaiveDateTime(datetime.datetime(2008, 1, 1), datetime.datetime(2009, 1, 1), ... force_day=3, force_second=42) >>> fdt.evaluate(2, None, False) # Actual code used by ``SomeFactory.build()`` datetime.datetime(2008, 5, 3, 12, 13, 42, 124848)
-
start_dt
¶ datetime.datetime
, the inclusive lower bound of generated datetimes
-
end_dt
¶ datetime.datetime
, the inclusive upper bound of generated datetimes
-
force_microsecond
¶ int or None; if set, forces the
microsecond
of generated datetime.
-
Custom fuzzy fields¶
Alternate fuzzy fields may be defined.
They should inherit from the BaseFuzzyAttribute
class, and override its
fuzz()
method.
Managing randomness¶
Using random
in factories allows to “fuzz” a program efficiently.
However, it’s sometimes required to reproduce a failing test.
factory.fuzzy
uses a separate instance of random.Random
,
and provides a few helpers for this:
-
factory.fuzzy.
get_random_state
()[source]¶ Call
get_random_state()
to retrieve the random generator’s current state.
-
factory.fuzzy.
set_random_state
(state)[source]¶ Use
set_random_state()
to set a custom state into the random generator (fetched fromget_random_state()
in a previous run, for instance)
-
factory.fuzzy.
reseed_random
(seed)[source]¶ The
reseed_random()
function allows to load a chosen seed into the random generator.
Custom BaseFuzzyAttribute
subclasses SHOULD
use factory.fuzzy._random
as a randomness source; this ensures that
data they generate can be regenerated using the simple state from
get_random_state()
.
Examples¶
Here are some real-world examples of using FactoryBoy.
Objects¶
First, let’s define a couple of objects:
class Account(object):
def __init__(self, username, email):
self.username = username
self.email = email
def __str__(self):
return '%s (%s)' % (self.username, self.email)
class Profile(object):
GENDER_MALE = 'm'
GENDER_FEMALE = 'f'
GENDER_UNKNOWN = 'u' # If the user refused to give it
def __init__(self, account, gender, firstname, lastname, planet='Earth'):
self.account = account
self.gender = gender
self.firstname = firstname
self.lastname = lastname
self.planet = planet
def __unicode__(self):
return u'%s %s (%s)' % (
unicode(self.firstname),
unicode(self.lastname),
unicode(self.account.accountname),
)
Factories¶
And now, we’ll define the related factories:
import datetime
import factory
import random
from . import objects
class AccountFactory(factory.Factory):
class Meta:
model = objects.Account
username = factory.Sequence(lambda n: 'john%s' % n)
email = factory.LazyAttribute(lambda o: '%s@example.org' % o.username)
date_joined = factory.LazyFunction(datetime.datetime.now)
class ProfileFactory(factory.Factory):
class Meta:
model = objects.Profile
account = factory.SubFactory(AccountFactory)
gender = factory.Iterator([objects.Profile.GENDER_MALE, objects.Profile.GENDER_FEMALE])
firstname = u'John'
lastname = u'Doe'
We have now defined basic factories for our Account
and Profile
classes.
If we commonly use a specific variant of our objects, we can refine a factory accordingly:
class FemaleProfileFactory(ProfileFactory):
gender = objects.Profile.GENDER_FEMALE
firstname = u'Jane'
user__username = factory.Sequence(lambda n: 'jane%s' % n)
Using the factories¶
We can now use our factories, for tests:
import unittest
from . import business_logic
from . import factories
from . import objects
class MyTestCase(unittest.TestCase):
def test_send_mail(self):
account = factories.AccountFactory()
email = business_logic.prepare_email(account, subject='Foo', text='Bar')
self.assertEqual(email.to, account.email)
def test_get_profile_stats(self):
profiles = []
profiles.extend(factories.ProfileFactory.create_batch(4))
profiles.extend(factories.FemaleProfileFactory.create_batch(2))
profiles.extend(factories.ProfileFactory.create_batch(2, planet="Tatooine"))
stats = business_logic.profile_stats(profiles)
self.assertEqual({'Earth': 6, 'Mars': 2}, stats.planets)
self.assertLess(stats.genders[objects.Profile.GENDER_FEMALE], 2)
Or for fixtures:
from . import factories
def make_objects():
factories.ProfileFactory.create_batch(size=50)
# Let's create a few, known objects.
factories.ProfileFactory(
gender=objects.Profile.GENDER_MALE,
firstname='Luke',
lastname='Skywalker',
planet='Tatooine',
)
factories.ProfileFactory(
gender=objects.Profile.GENDER_FEMALE,
firstname='Leia',
lastname='Organa',
planet='Alderaan',
)
Internals¶
ChangeLog¶
2.8.0 (2016-12-17)¶
New:
- issue #240: Call post-generation declarations in the order they were declared, thanks to Oleg Pidsadnyi.
- issue #309: Provide new options for SQLAlchemy session persistence
Bugfix:
- issue #334: Adjust for the package change in
faker
2.7.0 (2016-04-19)¶
New:
- issue #267: Add
factory.LazyFunction
to remove unneeded lambda parameters, thanks to Hervé Cauwelier.- issue #251: Add parameterized factories and
traits
- issue #256, issue #292: Improve error messages in corner cases
Removed:
- issue #278: Formally drop support for Python2.6
2.6.1 (2016-02-10)¶
New:
- issue #262: Allow optional forced flush on SQLAlchemy, courtesy of Minjung.
2.6.0 (2015-10-20)¶
New:
- Add
factory.FactoryOptions.rename
to help handle conflicting names (issue #206)- Add support for random-yet-realistic values through fake-factory, through the
factory.Faker
class.factory.Iterator
no longer begins iteration of its argument at import time, thus allowing to pass in a lazy iterator such as a Django queryset (i.efactory.Iterator(models.MyThingy.objects.all())
).- Simplify imports for ORM layers, now available through a simple
factory
import, atfactory.alchemy.SQLAlchemyModelFactory
/factory.django.DjangoModelFactory
/factory.mongoengine.MongoEngineFactory
.
Bugfix:
- issue #201: Properly handle custom Django managers when dealing with abstract Django models.
- issue #212: Fix
factory.django.mute_signals()
to handle Django’s signal caching- issue #228: Don’t load
django.apps.apps.get_model()
until required- issue #219: Stop using
mogo.model.Model.new()
, deprecated 4 years ago.
2.5.2 (2015-04-21)¶
Bugfix:
- Add support for Django 1.7/1.8
- Add support for mongoengine>=0.9.0 / pymongo>=2.1
2.5.1 (2015-03-27)¶
Bugfix:
- Respect custom managers in
DjangoModelFactory
(see issue #192)- Allow passing declarations (e.g
Sequence
) as parameters toFileField
andImageField
.
2.5.0 (2015-03-26)¶
New:
- Add support for getting/setting
factory.fuzzy
‘s random state (see issue #175, issue #185).- Support lazy evaluation of iterables in
factory.fuzzy.FuzzyChoice
(see issue #184).- Support non-default databases at the factory level (see issue #171)
- Make
factory.django.FileField
andfactory.django.ImageField
non-post_generation, i.e normal fields also available insave()
(see issue #141).
Bugfix:
- Avoid issues when using
factory.django.mute_signals()
on a base factory class (see issue #183).- Fix limitations of
factory.StubFactory
, that can now usefactory.SubFactory
and co (see issue #131).
Deprecation:
- Remove deprecated features from 2.4.0 (2014-06-21)
- Remove the auto-magical sequence setup (based on the latest primary key value in the database) for Django and SQLAlchemy; this relates to issues issue #170, issue #153, issue #111, issue #103, issue #92, issue #78. See https://github.com/FactoryBoy/factory_boy/commit/13d310f for technical details.
Warning
Version 2.5.0 removes the ‘auto-magical sequence setup’ bug-and-feature. This could trigger some bugs when tests expected a non-zero sequence reference.
Upgrading¶
Warning
Version 2.5.0 removes features that were marked as deprecated in v2.4.0.
All FACTORY_*
-style attributes are now declared in a class Meta:
section:
# Old-style, deprecated
class MyFactory(factory.Factory):
FACTORY_FOR = models.MyModel
FACTORY_HIDDEN_ARGS = ['a', 'b', 'c']
# New-style
class MyFactory(factory.Factory):
class Meta:
model = models.MyModel
exclude = ['a', 'b', 'c']
A simple shell command to upgrade the code would be:
# sed -i: inplace update
# grep -l: only file names, not matching lines
sed -i 's/FACTORY_FOR =/class Meta:\n model =/' $(grep -l FACTORY_FOR $(find . -name '*.py'))
This takes care of all FACTORY_FOR
occurences; the files containing other attributes to rename can be found with grep -R FACTORY .
2.4.1 (2014-06-23)¶
Bugfix:
- Fix overriding deeply inherited attributes (set in one factory, overridden in a subclass, used in a sub-sub-class).
2.4.0 (2014-06-21)¶
New:
- Add support for
factory.fuzzy.FuzzyInteger.step
, thanks to ilya-pirogov (issue #120)- Add
mute_signals()
decorator to temporarily disable some signals, thanks to ilya-pirogov (issue #122)- Add
FuzzyFloat
(issue #124)- Declare target model and other non-declaration fields in a
class Meta
section.
Deprecation:
Use of
FACTORY_FOR
and otherFACTORY
class-level attributes is deprecated and will be removed in 2.5. Those attributes should now declared within theclass Meta
attribute:For
factory.Factory
:
- Rename
FACTORY_FOR
tomodel
- Rename
ABSTRACT_FACTORY
toabstract
- Rename
FACTORY_STRATEGY
tostrategy
- Rename
FACTORY_ARG_PARAMETERS
toinline_args
- Rename
FACTORY_HIDDEN_ARGS
toexclude
For
factory.django.DjangoModelFactory
:
- Rename
FACTORY_DJANGO_GET_OR_CREATE
todjango_get_or_create
For
factory.alchemy.SQLAlchemyModelFactory
:
- Rename
FACTORY_SESSION
tosqlalchemy_session
2.3.1 (2014-01-22)¶
Bugfix:
- Fix badly written assert containing state-changing code, spotted by chsigi (issue #126)
- Don’t crash when handling objects whose __repr__ is non-pure-ascii bytes on Py2, discovered by mbertheau (issue #123) and strycore (issue #127)
2.3.0 (2013-12-25)¶
New:
- Add
FuzzyText
, thanks to jdufresne (issue #97)- Add
FuzzyDecimal
, thanks to thedrow (issue #94)- Add support for
EmbeddedDocument
, thanks to imiric (issue #100)
2.2.1 (2013-09-24)¶
Bugfix:
- Fixed sequence counter for
DjangoModelFactory
when a factory inherits from another factory relating to an abstract model.
2.2.0 (2013-09-24)¶
Bugfix:
- Removed duplicated
SQLAlchemyModelFactory
lurking infactory
(issue #83)- Properly handle sequences within object inheritance chains. If FactoryA inherits from FactoryB, and their associated classes share the same link, sequence counters will be shared (issue #93)
- Properly handle nested
SubFactory
overrides
New:
- The
DjangoModelFactory
now supports theFACTORY_FOR = 'myapp.MyModel'
syntax, making it easier to shove all factories in a single module (issue #66).- Add
factory.debug()
helper for easier backtrace analysis- Adding factory support for mongoengine with
MongoEngineFactory
.
2.1.0 (2013-06-26)¶
New:
- Add
FuzzyDate
thanks to saulshanabrook- Add
FuzzyDateTime
andFuzzyNaiveDateTime
.- Add a
factory_parent
attribute to theLazyStub
passed toLazyAttribute
, in order to access fields defined in wrapping factories.- Move
DjangoModelFactory
andMogoFactory
to their own modules (factory.django
andfactory.mogo
)- Add the
reset_sequence()
classmethod toFactory
to ease resetting the sequence counter for a given factory.- Add debug messages to
factory
logger.- Add a
reset()
method toIterator
(issue #63)- Add support for the SQLAlchemy ORM through
SQLAlchemyModelFactory
(issue #64, thanks to Romain Commandé)- Add
factory.django.FileField
andfactory.django.ImageField
hooks for related Django model fields (issue #52)
Bugfix
- Properly handle non-integer pks in
DjangoModelFactory
(issue #57).- Disable
RelatedFactory
generation when a specific value was passed (issue #62, thanks to Gabe Koscky)
Deprecation:
- Rename
RelatedFactory
‘sname
argument tofactory_related_name
(See issue #58)
2.0.2 (2013-04-16)¶
New:
- When
FACTORY_DJANGO_GET_OR_CREATE
is empty, useModel.objects.create()
instead ofModel.objects.get_or_create
.
2.0.1 (2013-04-16)¶
New:
- Don’t push
defaults
toget_or_create
whenFACTORY_DJANGO_GET_OR_CREATE
is not set.
2.0.0 (2013-04-15)¶
New:
- Allow overriding the base factory class for
make_factory()
and friends.- Add support for Python3 (Thanks to kmike and nkryptic)
- The default
type
forSequence
is nowint
- Fields listed in
FACTORY_HIDDEN_ARGS
won’t be passed to the associated class’ constructor- Add support for
get_or_create
inDjangoModelFactory
, throughFACTORY_DJANGO_GET_OR_CREATE
.- Add support for
fuzzy
attribute definitions.- The
Sequence
counter can be overridden when calling a generating function- Add
Dict
andList
declarations (Closes issue #18).
Removed:
- Remove associated class discovery
- Remove
InfiniteIterator
andinfinite_iterator()
- Remove
CircularSubFactory
- Remove
extract_prefix
kwarg to post-generation hooks.- Stop defaulting to Django’s
Foo.objects.create()
when “creating” instances- Remove STRATEGY_*
- Remove
set_building_function()
/set_creation_function()
1.3.0 (2013-03-11)¶
Warning
This version deprecates many magic or unexplicit features that will be removed in v2.0.0.
Please read the Upgrading section, then run your
tests with python -W default
to see all remaining warnings.
New¶
- Global:
- Rewrite the whole documentation
- Provide a dedicated
MogoFactory
subclass ofFactory
- The Factory class:
- Better creation/building customization hooks at
factory.Factory._build()
andfactory.Factory.create()
- Add support for passing non-kwarg parameters to a
Factory
wrapped class throughFACTORY_ARG_PARAMETERS
. - Keep the
FACTORY_FOR
attribute inFactory
classes
- Better creation/building customization hooks at
- Declarations:
- Allow
SubFactory
to solve circular dependencies between factories - Enhance
SelfAttribute
to handle “container” attribute fetching - Add a
getter
toIterator
declarations - A
Iterator
may be prevented from cycling by setting itscycle
argument toFalse
- Allow overriding default arguments in a
PostGenerationMethodCall
when generating an instance of the factory - An object created by a
DjangoModelFactory
will be saved again afterPostGeneration
hooks execution
- Allow
Pending deprecation¶
The following features have been deprecated and will be removed in an upcoming release.
- Declarations:
InfiniteIterator
is deprecated in favor ofIterator
CircularSubFactory
is deprecated in favor ofSubFactory
- The
extract_prefix
argument topost_generation()
is now deprecated
- Factory:
- Usage of
set_creation_function()
andset_building_function()
are now deprecated - Implicit associated class discovery is no longer supported, you must set the
FACTORY_FOR
attribute on allFactory
subclasses
- Usage of
Upgrading¶
This version deprecates a few magic or undocumented features. All warnings will turn into errors starting from v2.0.0.
In order to upgrade client code, apply the following rules:
- Add a
FACTORY_FOR
attribute pointing to the target class to eachFactory
, instead of relying on automagic associated class discovery - When using factory_boy for Django models, have each factory inherit from
DjangoModelFactory
- Replace
factory.CircularSubFactory('some.module', 'Symbol')
withfactory.SubFactory('some.module.Symbol')
- Replace
factory.InfiniteIterator(iterable)
withfactory.Iterator(iterable)
- Replace
@factory.post_generation()
with@factory.post_generation
- Replace
factory.set_building_function(SomeFactory, building_function)
with an override of the_build()
method ofSomeFactory
- Replace
factory.set_creation_function(SomeFactory, creation_function)
with an override of the_create()
method ofSomeFactory
1.1.4 (2012-06-19)¶
New:
- Add
use_strategy()
decorator to override aFactory
‘s default strategy- Improve test running (tox, python2.6/2.7)
- Introduce
PostGeneration
andRelatedFactory
1.1.2 (2012-02-25)¶
New:
- Add
Iterator
andInfiniteIterator
forFactory
attribute declarations.- Provide
generate()
andsimple_generate()
, that allow specifying the instantiation strategy directly. Also providesgenerate_batch()
andsimple_generate_batch()
.
1.1.1 (2012-02-24)¶
New:
- Add
build_batch()
,create_batch()
andstub_batch()
, to instantiate factories in batch
1.1.0 (2012-02-24)¶
New:
- Improve the
SelfAttribute
syntax to fetch sub-attributes using thefoo.bar
syntax;- Add
ContainerAttribute
to fetch attributes from the container of aSubFactory
.- Provide the
make_factory()
helper:MyClassFactory = make_factory(MyClass, x=3, y=4)
- Add
build()
,create()
,stub()
helpers
Bugfix:
- Allow classmethod/staticmethod on factories
Deprecation:
- Auto-discovery of
FACTORY_FOR
based on class name is now deprecated
1.0.4 (2011-12-21)¶
New:
- Improve the algorithm for populating a
Factory
attributes dict- Add
python setup.py test
command to run the test suite- Allow custom build functions
- Introduce
MOGO_BUILD
build function- Add support for inheriting from multiple
Factory
- Base
Factory
classes can now be declaredabstract
.- Provide
DjangoModelFactory
, whoseSequence
counter starts at the next free database id- Introduce
SelfAttribute
, a shortcut forfactory.LazyAttribute(lambda o: o.foo.bar.baz
.
Bugfix:
- Handle nested
SubFactory
- Share sequence counter between parent and subclasses
- Fix
SubFactory
/Sequence
interferences
1.0.1 (2011-05-13)¶
New:
- Allow
Factory
inheritance- Improve handling of custom build/create functions
Bugfix:
- Fix concurrency between
LazyAttribute
andSequence
Credits¶
- Initial version by Mark Sandstrom (2010)
- Developed by Raphaël Barrois since 2011
Ideas¶
This is a list of future features that may be incorporated into factory_boy:
- When a
Factory
is built or created, pass the calling context throughout the calling chain instead of custom solutions everywhere - Define a proper set of rules for the support of third-party ORMs
- Properly evaluate nested declarations (e.g
factory.fuzzy.FuzzyDate(start_date=factory.SelfAttribute('since'))
)