A test fixtures replacement for Python

Overview

factory_boy

Latest Version Supported Python versions Wheel status License

factory_boy is a fixtures replacement based on thoughtbot's factory_bot.

As a fixtures replacement tool, it aims to replace static, hard to maintain fixtures with easy-to-use factories for complex objects.

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="[email protected]",
            active=True,
            is_vip=True,
            address=address,
        )
        # etc.

factory_boy is designed to work well with various ORMs (Django, MongoDB, 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

Download

PyPI: https://pypi.org/project/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 a Python 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

ORM integration

factory_boy integration with Object Relational Mapping (ORM) tools is provided 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

More details can be found in the ORM section.

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.
# UserFactory must subclass an ORM base class, such as DjangoModelFactory.
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 faker library:

class RandomUserFactory(factory.Factory):
    class Meta:
        model = models.User

    first_name = factory.Faker('first_name')
    last_name = factory.Faker('last_name')
>>> RandomUserFactory()
<User: Lucy Murray>

Reproducible random values

The 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, located in the factory.random module:

import factory.random

def setup_test_environment():
    factory.random.reseed_random('my_awesome_project')
    # Other setup here

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: '{}.{}@example.com'.format(a.first_name, a.last_name).lower())
    date_joined = factory.LazyFunction(datetime.now)
>>> UserFactory().email
"[email protected]"

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{}@example.com'.format(n))

>>> UserFactory().email
'[email protected]'
>>> UserFactory().email
'[email protected]'

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

Support Policy

factory_boy supports active Python versions as well as PyPy3.

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.

Development dependencies can be installed in a virtualenv with:

$ pip install --editable '.[dev]'

All pull requests should pass the test suite, which can be launched simply with:

$ make testall

In order to test coverage, please use:

$ make coverage

To test with a specific framework version, you may use a tox target:

# list all tox environments
$ tox --listenvs

# run tests inside a specific environment
$ tox -e py36-django20-alchemy13-mongoengine017

Valid options are:

  • DJANGO for Django
  • MONGOENGINE for mongoengine
  • ALCHEMY for SQLAlchemy

To avoid running mongoengine tests (e.g no MongoDB server installed), run:

$ make SKIP_MONGOENGINE=1 test
Comments
  • factory.Trait() cannot get a factory.RelatedFactory override

    factory.Trait() cannot get a factory.RelatedFactory override

    Traits does not seem to allow a related factory override.

    Ex:

    class MyModelFactory(factory.django.DjangoModelFactory):
        class Meta:
              model = models.MyModel
        class Params:
            with_my_other_model = factory.Trait(
                 my_other_model=factory.RelatedFactory(MyOtherModelFactory, 'my_model')
            )
    

    It will complain my_other_model is not a valid field.

    That would be awesome.

    Bug 
    opened by jrobichaud 26
  • Allow SQLAlchemy factories to be committed on create

    Allow SQLAlchemy factories to be committed on create

    It seems like the general consensus is that one should only flush the current session during a test case, and roll back on each tear down. Due to the nature of my testing setup it is more convenient to commit instead of flush on create. Have you considered adding a meta flag similar to force_flush that would allow the session to be committed?

    for example:

    class SQLAlchemyModelFactory(base.Factory):
        """Factory for SQLAlchemy models. """
    
        _options_class = SQLAlchemyOptions
        class Meta:
            abstract = True
    
        @classmethod
        def _create(cls, model_class, *args, **kwargs):
            """Create an instance of the model, and save it to the database."""
            session = cls._meta.sqlalchemy_session
            obj = model_class(*args, **kwargs)
            session.add(obj)
    +       if cls._meta.force_commit:
    +          session.commit()
            elif cls._meta.force_flush:
                session.flush()
            return obj
    

    overriding SQLAlchemyModelFactory._create is my current solution. Any suggestions on how to properly have Factories be committed on creation is welcome! Thanks!

    Feature SQLAlchemy 
    opened by zyskowsk 22
  • SQLAlchemyModelFactory _create should at least flush the session

    SQLAlchemyModelFactory _create should at least flush the session

    SQLAlchemyModelFactory _create (https://github.com/rbarrois/factory_boy/blob/master/factory/alchemy.py#L45-50) should at least flush(I think flush in this context is better than commit) the SQLAlchemy session to produce the behaviour described in the docs:

    >>> session.query(User).all()
    []
    >>> UserFactory()
    <User: User 1>
    >>> session.query(User).all()
    [<User: User 1>]
    
    opened by vesauimonen 20
  • Managing randomness with faker is impossible

    Managing randomness with faker is impossible

    In the case when the fields are specified over faker providers, it's impossible to manage randomness because order of evaluation is not fixed (iterating a dict internally?). Example: Seed the random before of instantiation of

    class CompanyFactory(factory.DjangoModelFactory):
        class Meta:
            model = Company
    
        name = faker.company
        status = 'APR'
    

    with

    import factory
    faker = factory.faker.Faker._get_faker(locale='de_DE')
    faker.random.seed(0)
    

    returns the same result. But adding any other fuzzy field, for instance:

    faker.phone_number
    

    makes both fields to be filled in an unpredictable way. Maybe because the fields are evaluated in the dict iteration. It would be nice to have a work around at least.

    Bug NeedInfo 
    opened by scheparev-moberries 19
  • Add peewee support

    Add peewee support

    It was designed around my branch of peewee https://github.com/cam-stitt/peewee. I have a pull request waiting with my minor changes so hopefully it will be upstream soon. I have not yet completed any documentation regarding the implementation, it was merely a first attempt at getting one working for my own project.

    Edit: My changes have been merged into master for peewee. All tests now pass on master.

    Feature 
    opened by cam-stitt 17
  • DjangoModelFactory's

    DjangoModelFactory's "_setup_next_sequence" assumes that pk is an integer

    Offending code is here:

        @classmethod
        def _setup_next_sequence(cls):
            """Compute the next available PK, based on the 'pk' database field."""
    
            model = cls._associated_class  # pylint: disable=E1101
            manager = cls._get_manager(model)
    
            try:
                return 1 + manager.values_list('pk', flat=True
                    ).order_by('-pk')[0]
            except IndexError:
                return 1
    

    This problem didn't exist in factory_boy 1.3. My field that was using a non-integer PK is using a sequence, which worked fine previously:

    code = factory.Sequence(lambda n: str(n).zfill(3))
    

    I haven't dug into the code enough to know much about the changes that caused this problem, but ultimately I'd like to be able to use the sequence for the field again.

    Q&A Feature 
    opened by AndrewIngram 16
  • faker.py does not install via pip - strange stuff!!

    faker.py does not install via pip - strange stuff!!

    Hi guys,

    now this one is really strange. I just wanted to try the build-in faker and basically I am getting an attribute not found error:

    >>> import factory
    >>> factory.Faker('email')
    Traceback (most recent call last):
      File "<console>", line 1, in <module>
    AttributeError: 'module' object has no attribute 'Faker'
    

    20 minutes later I had a look at the virtualenv/site-packages path and noticed that the file "faker.py" is missing.

    And after doing an...

    pip uninstall factory-boy
    pip install factory-boy
    

    it is still missing!

    Any ideas whats going wrong?

    Q&A 
    opened by thebarty 15
  • bug with django 4.1

    bug with django 4.1

    Description

    we are using factory boy with django unit test but when we upgraded from 4.0.4 to 4.1.1 i got the following errors

    Traceback (most recent call last):
      File "/opt/atlassian/pipelines/agent/build/.venv/lib/python3.9/site-packages/django/db/models/query.py", line 928, in get_or_create
        return self.get(**kwargs), False
      File "/opt/atlassian/pipelines/agent/build/.venv/lib/python3.9/site-packages/cacheops/query.py", line 353, in get
        return qs._no_monkey.get(qs, *args, **kwargs)
      File "/opt/atlassian/pipelines/agent/build/.venv/lib/python3.9/site-packages/django/db/models/query.py", line 650, in get
        raise self.model.DoesNotExist(
    src.core.timezone.models.TimeZone.DoesNotExist: TimeZone matching query does not exist.
    During handling of the above exception, another exception occurred:
    Traceback (most recent call last):
      File "/opt/atlassian/pipelines/agent/build/.venv/lib/python3.9/site-packages/django/db/backends/utils.py", line 89, in _execute
        return self.cursor.execute(sql, params)
    psycopg2.errors.UniqueViolation: duplicate key value violates unique constraint "core_timezone_pkey"
    DETAIL:  Key (id)=(4) already exists.
    The above exception was the direct cause of the following exception:
    Traceback (most recent call last):
      File "/opt/atlassian/pipelines/agent/build/.venv/src/factory-boy/factory/django.py", line 143, in _get_or_create
        instance, _created = manager.get_or_create(*args, **key_fields)
      File "/opt/atlassian/pipelines/agent/build/.venv/lib/python3.9/site-packages/django/db/models/manager.py", line 85, in manager_method
        return getattr(self.get_queryset(), name)(*args, **kwargs)
      File "/opt/atlassian/pipelines/agent/build/.venv/lib/python3.9/site-packages/django/db/models/query.py", line 935, in get_or_create
        return self.create(**params), True
      File "/opt/atlassian/pipelines/agent/build/.venv/lib/python3.9/site-packages/django/db/models/query.py", line 671, in create
        obj.save(force_insert=True, using=self.db)
      File "/opt/atlassian/pipelines/agent/build/.venv/lib/python3.9/site-packages/django/db/models/base.py", line 831, in save
        self.save_base(
      File "/opt/atlassian/pipelines/agent/build/.venv/lib/python3.9/site-packages/django/db/models/base.py", line 882, in save_base
        updated = self._save_table(
      File "/opt/atlassian/pipelines/agent/build/.venv/lib/python3.9/site-packages/django/db/models/base.py", line 1025, in _save_table
        results = self._do_insert(
      File "/opt/atlassian/pipelines/agent/build/.venv/lib/python3.9/site-packages/django/db/models/base.py", line 1066, in _do_insert
        return manager._insert(
      File "/opt/atlassian/pipelines/agent/build/.venv/lib/python3.9/site-packages/django/db/models/manager.py", line 85, in manager_method
        return getattr(self.get_queryset(), name)(*args, **kwargs)
      File "/opt/atlassian/pipelines/agent/build/.venv/lib/python3.9/site-packages/django/db/models/query.py", line 1790, in _insert
        return query.get_compiler(using=using).execute_sql(returning_fields)
      File "/opt/atlassian/pipelines/agent/build/.venv/lib/python3.9/site-packages/django/db/models/sql/compiler.py", line 1657, in execute_sql
        cursor.execute(sql, params)
      File "/opt/atlassian/pipelines/agent/build/.venv/lib/python3.9/site-packages/django/db/backends/utils.py", line 103, in execute
        return super().execute(sql, params)
      File "/opt/atlassian/pipelines/agent/build/.venv/lib/python3.9/site-packages/cacheops/transaction.py", line 98, in execute
        result = self._no_monkey.execute(self, sql, params)
      File "/opt/atlassian/pipelines/agent/build/.venv/lib/python3.9/site-packages/django/db/backends/utils.py", line 67, in execute
        return self._execute_with_wrappers(
      File "/opt/atlassian/pipelines/agent/build/.venv/lib/python3.9/site-packages/django/db/backends/utils.py", line 80, in _execute_with_wrappers
        return executor(sql, params, many, context)
      File "/opt/atlassian/pipelines/agent/build/.venv/lib/python3.9/site-packages/django/db/backends/utils.py", line 89, in _execute
        return self.cursor.execute(sql, params)
      File "/opt/atlassian/pipelines/agent/build/.venv/lib/python3.9/site-packages/django/db/utils.py", line 91, in __exit__
        raise dj_exc_value.with_traceback(traceback) from exc_value
      File "/opt/atlassian/pipelines/agent/build/.venv/lib/python3.9/site-packages/django/db/backends/utils.py", line 89, in _execute
        return self.cursor.execute(sql, params)
    django.db.utils.IntegrityError: duplicate key value violates unique constraint "core_timezone_pkey"
    DETAIL:  Key (id)=(4) already exists.
    
    During handling of the above exception, another exception occurred:
    Traceback (most recent call last):
      File "/opt/atlassian/pipelines/agent/build/.venv/lib/python3.9/site-packages/django/test/testcases.py", line 1448, in setUpClass
        cls.setUpTestData()
      File "/opt/atlassian/pipelines/agent/build/adpp_backend/src/tests.py", line 47, in setUpTestData
        cls._admin_user = cls._create_user()
      File "/opt/atlassian/pipelines/agent/build/adpp_backend/src/tests.py", line 41, in _create_user
        return AdminUserFactory()
      File "/opt/atlassian/pipelines/agent/build/.venv/src/factory-boy/factory/base.py", line 40, in __call__
        return cls.create(**kwargs)
      File "/opt/atlassian/pipelines/agent/build/.venv/src/factory-boy/factory/base.py", line 528, in create
        return cls._generate(enums.CREATE_STRATEGY, kwargs)
      File "/opt/atlassian/pipelines/agent/build/.venv/src/factory-boy/factory/django.py", line 120, in _generate
        return super()._generate(strategy, params)
      File "/opt/atlassian/pipelines/agent/build/.venv/src/factory-boy/factory/base.py", line 465, in _generate
        return step.build()
      File "/opt/atlassian/pipelines/agent/build/.venv/src/factory-boy/factory/builder.py", line 260, in build
        step.resolve(pre)
      File "/opt/atlassian/pipelines/agent/build/.venv/src/factory-boy/factory/builder.py", line 201, in resolve
        self.attributes[field_name] = getattr(self.stub, field_name)
      File "/opt/atlassian/pipelines/agent/build/.venv/src/factory-boy/factory/builder.py", line 346, in __getattr__
        value = value.evaluate_pre(
      File "/opt/atlassian/pipelines/agent/build/.venv/src/factory-boy/factory/declarations.py", line 48, in evaluate_pre
        return self.evaluate(instance, step, context)
      File "/opt/atlassian/pipelines/agent/build/.venv/src/factory-boy/factory/declarations.py", line 411, in evaluate
        return step.recurse(subfactory, extra, force_sequence=force_sequence)
      File "/opt/atlassian/pipelines/agent/build/.venv/src/factory-boy/factory/builder.py", line 218, in recurse
        return builder.build(parent_step=self, force_sequence=force_sequence)
      File "/opt/atlassian/pipelines/agent/build/.venv/src/factory-boy/factory/builder.py", line 260, in build
        step.resolve(pre)
      File "/opt/atlassian/pipelines/agent/build/.venv/src/factory-boy/factory/builder.py", line 201, in resolve
        self.attributes[field_name] = getattr(self.stub, field_name)
      File "/opt/atlassian/pipelines/agent/build/.venv/src/factory-boy/factory/builder.py", line 346, in __getattr__
        value = value.evaluate_pre(
      File "/opt/atlassian/pipelines/agent/build/.venv/src/factory-boy/factory/declarations.py", line 48, in evaluate_pre
        return self.evaluate(instance, step, context)
      File "/opt/atlassian/pipelines/agent/build/.venv/src/factory-boy/factory/declarations.py", line 411, in evaluate
        return step.recurse(subfactory, extra, force_sequence=force_sequence)
      File "/opt/atlassian/pipelines/agent/build/.venv/src/factory-boy/factory/builder.py", line 218, in recurse
        return builder.build(parent_step=self, force_sequence=force_sequence)
      File "/opt/atlassian/pipelines/agent/build/.venv/src/factory-boy/factory/builder.py", line 260, in build
        step.resolve(pre)
      File "/opt/atlassian/pipelines/agent/build/.venv/src/factory-boy/factory/builder.py", line 201, in resolve
        self.attributes[field_name] = getattr(self.stub, field_name)
      File "/opt/atlassian/pipelines/agent/build/.venv/src/factory-boy/factory/builder.py", line 346, in __getattr__
        value = value.evaluate_pre(
      File "/opt/atlassian/pipelines/agent/build/.venv/src/factory-boy/factory/declarations.py", line 48, in evaluate_pre
        return self.evaluate(instance, step, context)
      File "/opt/atlassian/pipelines/agent/build/.venv/src/factory-boy/factory/declarations.py", line 411, in evaluate
        return step.recurse(subfactory, extra, force_sequence=force_sequence)
      File "/opt/atlassian/pipelines/agent/build/.venv/src/factory-boy/factory/builder.py", line 218, in recurse
        return builder.build(parent_step=self, force_sequence=force_sequence)
      File "/opt/atlassian/pipelines/agent/build/.venv/src/factory-boy/factory/builder.py", line 264, in build
        instance = self.factory_meta.instantiate(
      File "/opt/atlassian/pipelines/agent/build/.venv/src/factory-boy/factory/base.py", line 317, in instantiate
        return self.factory._create(model, *args, **kwargs)
      File "/opt/atlassian/pipelines/agent/build/.venv/src/factory-boy/factory/django.py", line 166, in _create
        return cls._get_or_create(model_class, *args, **kwargs)
      File "/opt/atlassian/pipelines/agent/build/.venv/src/factory-boy/factory/django.py", line 147, in _get_or_create
        for lookup, value in cls._original_params.items()
    AttributeError: type object 'TimeZoneFactory' has no attribute '_original_params'
    

    To Reproduce

    Share how the bug happened:

    Model / Factory code
    # Include your factories and models here
    import factory
    from factory.faker import faker
    
    from .models import TimeZone
    
    fake = faker.Faker()
    
    
    class TimeZoneFactory(factory.django.DjangoModelFactory):
        class Meta:
            model = TimeZone
    
        name = factory.Sequence(lambda n: f'{fake.name()}_{n}')
    
    #models
    
    class TimeZone(BaseModel):
        """
        TimeZone
        """
    
        class Meta:
            db_table = 'core_timezone'
            verbose_name = _('time zone')
            verbose_name_plural = _('time zones')
    
        name = models.TextField(_('name'), unique=True)
    
    
    The issue

    this factory is used everywhere in the project, not sure why this error appeared after upgrading to django 4.1.1

    opened by hishamkaram 14
  • ImportError: The ``fake-factory`` package is now called ``Faker``.

    ImportError: The ``fake-factory`` package is now called ``Faker``.

    Trying to import factory, but it looks like since yesterday, one of the required packages has changed it's name:

        import factory
      File "/venv/lib64/python3.5/site-packages/factory/__init__.py", line 46, in <module>
        from .faker import Faker
      File "/venv/lib64/python3.5/site-packages/factory/faker.py", line 41, in <module>
        import faker
      File "/venv/lib64/python3.5/site-packages/faker/__init__.py", line 7, in <module>
        raise ImportError(error)
    ImportError: The ``fake-factory`` package is now called ``Faker``.
    

    Edit: I now see there's already a commit to fix this. Any chance of a release to pypi?

    opened by drodger 14
  • How to handle 'self' Foreign key relation in factory boy

    How to handle 'self' Foreign key relation in factory boy

    Hello,

    I've a django model where i'm using Foreign key with itself. Can you please guide me how i can replicate self relation in factory boy class. e.g.

    class A(models.Model):
        name = models.CharField(max_length=255)
        parent = models.ForeignKey('self', blank=True, null=True)
    

    Please guide how i can create a factory for such type of relations.

    Thanks.

    Q&A 
    opened by muzaffaryousaf 14
  • Include session getter support

    Include session getter support

    As our team tries to migrate our tests to use factory_boy, we found tricky to set the DB sessions during the tests runtime, mocking was tricky and nasty. I found this issue, which closely resemble the problem we are currently facing, and decided to give a shot.

    The idea is to allow passing a callable returning the SQLAlchemy session in a Meta attribute called sqlalchemy_session_factory. This has several use-cases, allowing control of the session on runtime.

    This is my first OSS contribution ever, super excited with it. Any feedback is appreciated :)

    Fixes https://github.com/FactoryBoy/factory_boy/issues/304

    SQLAlchemy 
    opened by hugopellissari 13
  • Is subclassing `factory.base.Factory` for custom ORM supported?

    Is subclassing `factory.base.Factory` for custom ORM supported?

    The documentation does not mention a supported way to add a factory with custom _create and _build functions. Am I supposed to subclass factory.base.Factory?

    This works:

    from factory import base
    
    class BaseFactory(base.Factory):
        class Meta:
            abstract = True
    
        @classmethod
        def _build(cls, model_class, *args, **kwargs):
            return model_class(*args, **kwargs)
    
        @classmethod
        def _create(cls, model_class, *args, **kwargs):
            # TODO Use my ORM
            return model_class(*args, **kwargs)
    

    (Taken from https://github.com/FactoryBoy/factory_boy/blob/master/factory/mogo.py)

    opened by WilliamDEdwards 0
  • DictFactory does not override _setup_next_sequence

    DictFactory does not override _setup_next_sequence

    Description

    Overriding _setup_next_sequence does not work when subclassing DictFactory, but I get it to work for other factories subclassing DjangoModelFactory.

    Model / Factory code
    import factory
    
    
    class TestDictFactory(factory.DictFactory):
        @classmethod
        def _setup_next_sequence(cls) -> int:
            return 10
    
        number = factory.Sequence(lambda n: n)
    

    Test case

    A simple test case where the sequence should start from 10

    def test_dict_factory():
        test = TestDictFactory()
        assert test["number"] == 10
    
    Output
    =========================================== test session starts ===========================================
    platform darwin -- Python 3.10.6, pytest-7.2.0, pluggy-1.0.0
    django: ...
    rootdir: ...
    plugins: Faker-15.1.3, mock-3.10.0, django-4.5.2
    collected 61 items / 60 deselected / 1 selected
    
    tests/test_factories.py F                                                                [100%]
    
    ================================================ FAILURES =================================================
    ____________________________________________ test_dict_factory ____________________________________________
    
        def test_dict_factory():
            test = TestDictFactory()
    >       assert test["number"] == 10
    E       assert 0 == 10
    
    tests/test_factories.py:23: AssertionError
    
    
    Bug 
    opened by martsime 0
  • Example Django test code references missing member

    Example Django test code references missing member

    https://github.com/FactoryBoy/factory_boy/blob/edb6c2ee76c842ab9d6c5435ef1e63527c877507/examples/django_demo/generic_foreignkey/tests.py#L19 results in Pylance complaining:

    Cannot access member "tag" for type "TaggedUserFactory"
      Member "tag" is unknown
    

    Similarly: https://github.com/FactoryBoy/factory_boy/blob/edb6c2ee76c842ab9d6c5435ef1e63527c877507/examples/django_demo/generic_foreignkey/tests.py#L22 results in Pylance complaining:

    Argument of type "SubFactory" cannot be assigned to parameter "model" of type "Type[Model] | Model" in function "get_for_model"
      Type "SubFactory" cannot be assigned to type "Type[Model] | Model"
        Type "SubFactory" cannot be assigned to type "Type[Model]"
        "SubFactory" is incompatible with "Model"
    

    I poked at a bit, resulting in #988, but I don't use Django much, so still wasn't quite sure what needed to happen to fix these.

    opened by jeffwidman 0
  • Support Django 4.1 Asynchronous ORM interface

    Support Django 4.1 Asynchronous ORM interface

    To instantiate a Django's models with factory_boy within an async context, we currently need to wrap it with sync_to_async, e.g. workspace = await sync_to_async(WorkspaceFactory)().

    It would be awesome if DjangoModelFactory could provide a .acreate() method, which either encapsulates the call to sync_to_async or even better, uses the new Asynchronous ORM interface from Django 4.1.

    The latter would automatically benefit from any future development of Django's async ORM.

    opened by qcoumes 0
  • django_get_or_create with SubFactory

    django_get_or_create with SubFactory

    Description

    The django_get_or_create feature is working well when the concerned Factory is used as a BaseFactory but not when it's used as a SubFactory When used as a SubFactory, the following error occurs : AttributeError: type object 'CurrencyFactory' has no attribute '_original_params'

    To Reproduce

    With the below code, the error will occur randomly when the same currency code/name is used.

    To reproduce it systematically, just change the PairFactory with the following line :

    base_currency = factory.SubFactory(CurrencyFactory, name="Bitcoin")
    

    It will force the IntegrityError in the Currency Factory, so it will try the get_or_create feature. And then call the PairFactory() two times and voila.

    E           AttributeError: type object 'CurrencyFactory' has no attribute '_original_params'
    
    ../../../.cache/pypoetry/virtualenvs/belfort-backend-KmGKt2wB-py3.10/lib/python3.10/site-packages/factory/django.py:151: AttributeError
    
    Model / Factory code
    # Models
    class Currency(models.Model):
        name = models.CharField(max_length=100, unique=True)
        symbol = models.CharField(max_length=20, unique=True)
    
    class Pair(models.Model):
        base_currency = models.ForeignKey(
            Currency, on_delete=models.CASCADE, related_name="pairs_as_base"
        )
        quoted_currency = models.ForeignKey(
            Currency, on_delete=models.CASCADE, related_name="pairs_as_quoted"
        )
    
    # Factories
    class CurrencyFactory(factory.django.DjangoModelFactory):
        class Meta:
            model = "Currency"
            django_get_or_create = ("name", "symbol")
    
        name = factory.Faker("cryptocurrency_name")
        symbol = factory.Faker("cryptocurrency_code")
    
    class PairFactory(factory.django.DjangoModelFactory):
        class Meta:
            model = "Pair"
    
        base_currency = factory.SubFactory(CurrencyFactory)
        quoted_currency = factory.SubFactory(CurrencyFactory)
    

    Notes

    I believe this is linked to the _generate() method which is not called for SubFactory, while the _original_params is set only in this method.

    Bug Django 
    opened by VRohou 6
Owner
FactoryBoy project
Contributors to the factory_boy Python library, and related projects
FactoryBoy project
A drop-in replacement for Django's runserver.

About A drop in replacement for Django's built-in runserver command. Features include: An extendable interface for handling things such as real-time l

David Cramer 1.3k Dec 15, 2022
Green is a clean, colorful, fast python test runner.

Green -- A clean, colorful, fast python test runner. Features Clean - Low redundancy in output. Result statistics for each test is vertically aligned.

Nathan Stocks 756 Dec 22, 2022
splinter - python test framework for web applications

splinter - python tool for testing web applications splinter is an open source tool for testing web applications using Python. It lets you automate br

Cobra Team 2.6k Dec 27, 2022
create custom test databases that are populated with fake data

About Generate fake but valid data filled databases for test purposes using most popular patterns(AFAIK). Current support is sqlite, mysql, postgresql

Emir Ozer 2.2k Jan 6, 2023
Scalable user load testing tool written in Python

Locust Locust is an easy to use, scriptable and scalable performance testing tool. You define the behaviour of your users in regular Python code, inst

Locust.io 20.4k Jan 8, 2023
A cross-platform GUI automation Python module for human beings. Used to programmatically control the mouse & keyboard.

PyAutoGUI PyAutoGUI is a cross-platform GUI automation Python module for human beings. Used to programmatically control the mouse & keyboard. pip inst

Al Sweigart 7.6k Jan 1, 2023
Let your Python tests travel through time

FreezeGun: Let your Python tests travel through time FreezeGun is a library that allows your Python tests to travel through time by mocking the dateti

Steve Pulec 3.5k Jan 9, 2023
HTTP client mocking tool for Python - inspired by Fakeweb for Ruby

HTTPretty 1.0.5 HTTP Client mocking tool for Python created by Gabriel Falcão . It provides a full fake TCP socket module. Inspired by FakeWeb Github

Gabriel Falcão 2k Jan 6, 2023
A utility for mocking out the Python Requests library.

Responses A utility library for mocking out the requests Python library. Note Responses requires Python 2.7 or newer, and requests >= 2.0 Installing p

Sentry 3.8k Jan 2, 2023
Faker is a Python package that generates fake data for you.

Faker is a Python package that generates fake data for you. Whether you need to bootstrap your database, create good-looking XML documents, fill-in yo

Daniele Faraglia 15.2k Jan 1, 2023
Mimesis is a high-performance fake data generator for Python, which provides data for a variety of purposes in a variety of languages.

Mimesis - Fake Data Generator Description Mimesis is a high-performance fake data generator for Python, which provides data for a variety of purposes

Isaak Uchakaev 3.8k Jan 1, 2023
Coroutine-based concurrency library for Python

gevent Read the documentation online at http://www.gevent.org. Post issues on the bug tracker, discuss and ask open ended questions on the mailing lis

gevent 5.9k Dec 28, 2022
Radically simplified static file serving for Python web apps

WhiteNoise Radically simplified static file serving for Python web apps With a couple of lines of config WhiteNoise allows your web app to serve its o

Dave Evans 2.1k Jan 8, 2023
livereload server in python (MAINTAINERS NEEDED)

LiveReload Reload webpages on changes, without hitting refresh in your browser. Installation python-livereload is for web developers who know Python,

Hsiaoming Yang 977 Dec 14, 2022
A screamingly fast Python 2/3 WSGI server written in C.

bjoern: Fast And Ultra-Lightweight HTTP/1.1 WSGI Server A screamingly fast, ultra-lightweight WSGI server for CPython 2 and CPython 3, written in C us

Jonas Haag 2.9k Dec 21, 2022
Waitress - A WSGI server for Python 2 and 3

Waitress Waitress is a production-quality pure-Python WSGI server with very acceptable performance. It has no dependencies except ones which live in t

Pylons Project 1.2k Dec 30, 2022
Python HTTP Server

Python HTTP Server Preview Languange and Code Editor: How to run? Download the zip first. Open the http.py and wait 1-2 seconds. You will see __pycach

SonLyte 16 Oct 21, 2021
PyQaver is a PHP like WebServer for Python.

PyQaver is a PHP like WebServer for Python.

Dev Bash 7 Apr 25, 2022
Robyn is an async Python backend server with a runtime written in Rust, btw.

Robyn is an async Python backend server with a runtime written in Rust, btw. Python server running on top of of Rust Async RunTime. Installation

Sanskar Jethi 1.8k Dec 30, 2022