Skip to content

FactoryBoy

Factoryboy is a fixtures replacement library to generate fake data for your program. As it's designed to work well with different ORMs (Django, SQLAlchemy, Mongo) it serves the purpose of building real objects for your tests.

If you use pydantic, pydantic-factories does all this automatically for you!

Install

pip install factory_boy

Or add it to the project requirements.txt.

Define a factory class

Use the following code to generate a factory class for the User SQLAlchemy class.

from {{ program_name }} import models

import factory

# XXX If you add new Factories remember to add the session in conftest.py


class UserFactory(factory.alchemy.SQLAlchemyModelFactory):
    """
    Class to generate a fake user element.
    """
    id = factory.Sequence(lambda n: n)
    name = factory.Faker('name')

    class Meta:
        model = models.User
        sqlalchemy_session_persistence = 'commit'

As stated in the comment, and if you are using the proposed python project template, remember to add new Factories in conftest.py.

Use the factory class

  • Create an instance.

    UserFactory.create()
    
  • Create an instance with a defined attribute.

    UserFactory.create(name='John')
    
  • Create 100 instances of objects with an attribute defined.

    UserFactory.create_batch(100, name='John')
    

Define attributes

I like to use the faker integration of factory boy to generate most of the attributes.

Generate numbers

Sequential numbers

Ideal for IDs

id = factory.Sequence(lambda n: n)

Random number or integer

author_id = factory.Faker('random_number')

If you want to limit the number of digits use factory.Faker('random_number', digits=3)

Random float

score = factory.Faker('pyfloat')

Generate strings

Word

default = factory.Faker('word')

Word from a list

user = factory.Faker('word', ext_word_list=[None, 'value_1', 'value_2'])

Word from Enum choices

First install the Enum provider

factory.Faker.add_provider(EnumProvider)

class EntityFactory(factory.Factory):  # type: ignore
    state = factory.Faker("enum", enum_cls=EntityState)

Sentences

description = factory.Faker('sentence')

Names

name = factory.Faker('name')

Urls

url = factory.Faker('url')

Files

file_path = factory.Faker('file_path')

Generate Datetime

factory.Faker('date_time')

Generate bool

factory.Faker('pybool')

Generate your own attributes

Using custom Faker providers

Using lazy_attributes

Use lazy_attribute decorator.

If you want to use Faker inside a lazy_attribute use .generate({}) at the end of the attribute.

In newer versions of Factoryboy you can't use Faker inside a lazy attribute

As the Faker object doesn't have the generate method.

    @factory.lazy_attribute
    def due(self):
        if random.random() > 0.5:
            return factory.Faker('date_time').generate({})

Define relationships

Factory Inheritance

class ContentFactory(factory.alchemy.SQLAlchemyModelFactory):
    id = factory.Sequence(lambda n: n)
    title = factory.Faker('sentence')

    class Meta:
        model = models.Content
        sqlalchemy_session_persistence = 'commit'


class ArticleFactory(ContentFactory):
    body = factory.Faker('sentence')

    class Meta:
        model = models.Article
        sqlalchemy_session_persistence = 'commit'

Dependent objects direct ForeignKey

When one attribute is actually a complex field (e.g a ForeignKey to another Model), use the SubFactory declaration. Assuming the following model definition:

class Author(Base):
    id = Column(String, primary_key=True)
    contents = relationship('Content', back_populates='author')


class Content(Base):
    id = Column(Integer, primary_key=True, doc='Content ID')
    author_id = Column(String, ForeignKey(Author.id))
    author = relationship(Author, back_populates='contents')

The related factories would be:

class AuthorFactory(factory.alchemy.SQLAlchemyModelFactory):
    id = factory.Faker('word')

    class Meta:
        model = models.Author
        sqlalchemy_session_persistence = 'commit'


class ContentFactory(factory.alchemy.SQLAlchemyModelFactory):
    id = factory.Sequence(lambda n: n)
    author = factory.SubFactory(AuthorFactory)

    class Meta:
        model = models.Content
        sqlalchemy_session_persistence = 'commit'

Automatically generate a factory from a pydantic model

Sadly it's not yet supported, it will at some point though. If you're interested in following this path, you can start with mgaitan snippet for dataclasses.

References