factoryboy

repository·master·Indexed 26 days ago

https://github.com/factoryboy/factory_boy

A Python library used as a replacement for static test fixtures. It provides a declarative syntax to create complex, customized objects for testing, with specialized support for ORMs including Django, SQLAlchemy, and MongoDB. Features include sequence generation, lazy attributes, sub-factories, and random data generation via the factory.fuzzy module.

Tokens
20.9K
Snippets
68
Records
91
Agent score
87%

What's inside factory_boy

  1. Set an initial sequence value automatically

    master

    To prevent conflicts with existing data in a database, you can override the _setup_next_sequence class method in your factory. This method is called automatically upon the first instantiation of the factory. A common pattern is to query the database for the current maximum ID and return that 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
  2. Fuzzyize Django model field choices

    master

    When using factory.fuzzy.FuzzyChoice with Django models that define choices as a list of tuples (e.g., (value, label)), use the getter keyword argument to extract only the value needed for the database field.

    class UserFactory(factory.Factory):
        class Meta:
            model = User
    
        # CATEGORY_CHOICES is a list of (key, title) tuples
        category = factory.fuzzy.FuzzyChoice(User.CATEGORY_CHOICES, getter=lambda c: c[0])
  3. Use Fuzzy attributes for random data generation

    master

    The factory.fuzzy module provides several classes for generating random values in factories.

    Note: Most built-in fuzzers are deprecated in favor of factory.Faker equivalents. Use factory.fuzzy primarily if you specifically need these built-in random declarations or are maintaining older code.

    To use these features, you must import the module:

    import factory.fuzzy
  4. Use Post-generation hooks to perform complex processing

    master

    Post-generation hooks allow you to perform additional method calls or complex processing (like creating related objects or side effects) after a factory has generated an object.

    Available tools:

    • PostGenerationMethodCall: Calls a specific method on the generated object.
    • PostGeneration: Calls a given function with the generated object as an argument.
    • post_generation: A decorator for defining post-generation functions.
    • RelatedFactory: Builds or creates a single related factory after the base factory.
    • RelatedFactoryList: Builds or creates a list of related factories after the base factory.

    Hooks are executed in the order they are declared in the factory class.

  5. Create a basic factory with factory.Factory

    master

    To create a factory, subclass factory.Factory and define a class Meta containing the model attribute. You can then define default attribute values as class attributes. You can override these defaults by passing keyword arguments when calling the factory.

    import factory
    from . import base
    
    class UserFactory(factory.Factory):
        class Meta:
            model = base.User
    
        firstname = "John"
        lastname = "Doe"
    
    # Usage
    john = UserFactory()
    # Overriding defaults
    jack = UserFactory(firstname="Jack")
  6. Define basic factories using factory.Factory

    master

    To create factories for plain Python objects, subclass factory.Factory and define a Meta class with the model attribute set to your target class. You can use various factory attributes to define field values:

    • factory.Sequence: Generates unique values using a sequence number.
    • factory.LazyAttribute: Computes a value based on the instance being created.
    • factory.LazyFunction: Calls a function to provide a value.
    • factory.SubFactory: Creates a relationship to another factory.
    • factory.Iterator: Cycles through a provided list of values.
    import datetime
    import factory
    
    class AccountFactory(factory.Factory):
        class Meta:
            model = 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 = Profile
    
        account = factory.SubFactory(AccountFactory)
        gender = factory.Iterator(['m', 'f'])
        firstname = 'John'
        lastname = 'Doe'
  7. Force a sequence value on a per-call basis

    master

    To ensure specific values for attributes using factory.Sequence during a single instantiation (useful for reproducible tests), pass the __sequence keyword argument to the factory call. This overrides the counter for that specific instance without affecting the global factory counter.

    class AccountFactory(factory.Factory):
        class Meta:
            model = Account
        uid = factory.Sequence(lambda n: n)
        name = "Test"
    
    # Force the sequence to 10 for this instance
    obj1 = AccountFactory(name="John Doe", __sequence=10)
    print(obj1.uid)  # 10
    
    # The base counter remains unchanged
    obj2 = AccountFactory(name="Jane Doe")
    print(obj2.uid)  # 1
  8. Manage randomness for reproducible tests

    master

    To prevent test flakiness caused by random values in factory.Faker or factory.fuzzy objects, you can seed the random engine.

    Seeding the engine

    Use factory.random.reseed_random(seed) to apply a global seed at the start of your tests.

    Reproducing unseeded tests

    If you want to capture and reuse a specific random state (e.g., for debugging a failing test), use factory.random.get_random_state() to retrieve the current state and factory.random.set_random_state(state) to restore it. It is recommended to pass the state around as a base64-encoded pickle dump.

  9. Define Parameters and Traits in Factories

    master

    Factories can use Params to handle complex object states.

    Simple Parameters

    Parameters defined in class Params are available to the factory logic but are not passed to the final model class (similar to exclude).

    Traits

    A Trait allows you to group multiple field overrides under a single parameter. Traits are activated by passing a boolean (or truthy value) to the parameter name.

    Note: When overriding a Trait in a subclass, you must replace the entire declaration.

    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),
            )
    
    # Usage
    order = OrderFactory(shipped=True)