Factory Configuration#

Factories can be configured by setting special dunder (double underscore) class attributes. You can read the reference for these in the API reference for BaseFactory. Below we discuss some configuration options in some depth.

Seeding Randomness#

Seeding the factory’s ‘random.Random’#
from dataclasses import dataclass

from polyfactory.factories import DataclassFactory


@dataclass
class Person:
    name: str
    age: float
    height: float
    weight: float


class PersonFactory(DataclassFactory[Person]):
    __random_seed__ = 1

    @classmethod
    def name(cls) -> str:
        return cls.__random__.choice(["John", "Alice", "George"])


def test_random_seed() -> None:
    # the outcome of 'factory.__random__.choice' is deterministic, because Random has been seeded with a set value.
    assert PersonFactory.build().name == "John"
    assert PersonFactory.build().name == "George"
    assert PersonFactory.build().name == "John"

Seeding randomness allows you to control the random generation of values produced by the factory. This affects all random.Random methods as well as faker.

Setting Random#

Setting the factory’s ‘random.Random’#
from dataclasses import dataclass
from random import Random

from polyfactory.factories import DataclassFactory


@dataclass
class Person:
    name: str
    age: float
    height: float
    weight: float


class PersonFactory(DataclassFactory[Person]):
    __random__ = Random(10)

    @classmethod
    def name(cls) -> str:
        return cls.__random__.choice(["John", "Alice", "George"])


def test_setting_random() -> None:
    # the outcome of 'factory.__random__.choice' is deterministic, because Random is configured with a set value.
    assert PersonFactory.build().name == "George"
    assert PersonFactory.build().name == "John"
    assert PersonFactory.build().name == "Alice"

This configuration option is functionally identical to the previous, with the difference being that here we are setting the actual instance of random.Random. This is useful when embedding factories inside more complex logic, such as in other libraries, as well as when factories are being dynamically generated.

Setting Faker#

Setting the factory’s ‘Faker’#
from dataclasses import dataclass

from faker import Faker

from polyfactory.factories import DataclassFactory


@dataclass
class Person:
    name: str
    age: float
    height: float
    weight: float


class PersonFactory(DataclassFactory[Person]):
    __faker__ = Faker(locale="es_ES")

    __random_seed__ = 10

    @classmethod
    def name(cls) -> str:
        return cls.__faker__.name()


def test_setting_faker() -> None:
    # the outcome of faker deterministic because we seeded random, and it uses a spanish locale.
    assert PersonFactory.build().name == "Alejandra Romeu-Tolosa"

In the above example we are setting the factory’s instance of Faker and configure its locale to Spanish. Because we are also setting the random seed value, the results of the test are deterministic.

Note

To understand why we are using a classmethod here, see the documentation about factory fields.

Persistence Handlers#

Factory classes have four optional persistence methods:

  • .create_sync(**kwargs) - builds and persists a single instance of the factory’s model synchronously

  • .create_batch_sync(size: int, **kwargs) - builds and persists a list of size n instances synchronously

  • .create_async(**kwargs) - builds and persists a single instance of the factory’s model asynchronously

  • .create_batch_async(size: int, **kwargs) - builds and persists a list of size n instances asynchronously

To use these methods, you must first specify a sync and/or async persistence handlers for the factory:

Defining and using persistence handlers.#
from asyncio import sleep
from dataclasses import dataclass
from typing import Dict, List
from uuid import UUID

from polyfactory import AsyncPersistenceProtocol, SyncPersistenceProtocol
from polyfactory.factories import DataclassFactory


@dataclass
class Person:
    id: UUID
    name: str


# we will use a dictionary to persist values for the example
mock_db: Dict[UUID, Person] = {}


class SyncPersistenceHandler(SyncPersistenceProtocol[Person]):
    def save(self, data: Person) -> Person:
        # do stuff here to persist the value, such as use an ORM or ODM, cache in redis etc.
        # in our case we simply save it in the dictionary.
        mock_db[data.id] = data
        return data

    def save_many(self, data: List[Person]) -> List[Person]:
        # same as for save, here we should store the list in persistence.
        # in this case, we use the same dictionary.
        for person in data:
            mock_db[person.id] = person
        return data


class AsyncPersistenceHandler(AsyncPersistenceProtocol[Person]):
    async def save(self, data: Person) -> Person:
        # do stuff here to persist the value using an async method, such as an async ORM or ODM.
        # in our case we simply save it in the dictionary and add a minimal sleep to mock async.
        mock_db[data.id] = data
        await sleep(0.0001)
        return data

    async def save_many(self, data: List[Person]) -> List[Person]:
        # same as for the async save, here we should store the list in persistence using async logic.
        # we again store in dict, and mock async using sleep.
        for person in data:
            mock_db[person.id] = person
        await sleep(0.0001)
        return data


class PersonFactory(DataclassFactory[Person]):
    __sync_persistence__ = SyncPersistenceHandler
    __async_persistence__ = AsyncPersistenceHandler


def test_sync_persistence_build() -> None:
    person_instance = PersonFactory.create_sync()
    assert mock_db[person_instance.id] is person_instance


def test_sync_persistence_batch() -> None:
    person_batch = PersonFactory.create_batch_sync(10)
    for person_instance in person_batch:
        assert mock_db[person_instance.id] is person_instance


async def test_async_persistence_build() -> None:
    person_instance = await PersonFactory.create_async()
    assert mock_db[person_instance.id] is person_instance


async def test_async_persistence_batch() -> None:
    person_batch = await PersonFactory.create_batch_async(10)
    for person_instance in person_batch:
        assert mock_db[person_instance.id] is person_instance

With the persistence handlers in place, you can now use all persistence methods.

Note

You do not need to define both persistence handlers. If you will only use sync or async persistence, you only need to define the respective handler to use these methods.

Defining Default Factories#

As explained in the section about dynamic factory generation in declaring factories, factories generate new factories for supported types dynamically. This process requires no intervention from the user. Once a factory is generated, it is then cached and reused - when the same type is used.

For example, when build is called for the PersonFactory below, a PetFactory will be dynamically generated and reused:

Dynamic factory generation#
from dataclasses import dataclass
from datetime import date, datetime
from enum import Enum
from typing import Any, Dict, List, Union
from uuid import UUID

from polyfactory.factories import DataclassFactory


class Species(str, Enum):
    CAT = "Cat"
    DOG = "Dog"


@dataclass
class Pet:
    name: str
    species: Species
    sound: str


@dataclass
class Person:
    id: UUID
    name: str
    hobbies: List[str]
    age: Union[float, int]
    birthday: Union[datetime, date]
    pets: List[Pet]
    assets: List[Dict[str, Dict[str, Any]]]


class PersonFactory(DataclassFactory[Person]):
    ...


def test_dynamic_factory_generation() -> None:
    person_instance = PersonFactory.build()
    assert len(person_instance.pets) > 0
    assert isinstance(person_instance.pets[0], Pet)

You can also control the default factory for a type by declaring a factory as the type default:

Setting a factory as a type default.#
from dataclasses import dataclass
from datetime import date, datetime
from enum import Enum
from typing import Any, Dict, List, Union
from uuid import UUID

from polyfactory import Use
from polyfactory.factories import DataclassFactory


class Species(str, Enum):
    CAT = "Cat"
    DOG = "Dog"


@dataclass
class Pet:
    name: str
    species: Species
    sound: str


@dataclass
class Person:
    id: UUID
    name: str
    hobbies: List[str]
    age: Union[float, int]
    birthday: Union[datetime, date]
    pets: List[Pet]
    assets: List[Dict[str, Dict[str, Any]]]


class PetFactory(DataclassFactory[Pet]):
    __set_as_default_factory_for_type__ = True

    name = Use(DataclassFactory.__random__.choice, ["Roxy", "Spammy", "Moshe"])


class PersonFactory(DataclassFactory[Person]):
    ...


def test_default_pet_factory() -> None:
    person_instance = PersonFactory.build()
    assert len(person_instance.pets) > 0
    assert person_instance.pets[0].name in ["Roxy", "Spammy", "Moshe"]

Randomized Collection Length#

Set of fields that allow you to generate a collection with random lengths. By default only one item is generated.

Randomized collection length#
from dataclasses import dataclass
from typing import Tuple

from polyfactory.factories import DataclassFactory


@dataclass
class Owner:
    cars: Tuple[str, ...]


class OwnerFactory(DataclassFactory[Owner]):
    __randomize_collection_length__ = True
    __min_collection_length__ = 2
    __max_collection_length__ = 5


def test_randomized_collection_length() -> None:
    owner = OwnerFactory.build()
    assert 2 <= len(owner.cars) <= 5

Allow None Optionals#

Allow None to be generated as a value for types marked as optional. When set to True, the outputted value will be randomly chosen between None and other allowed types. By default, this is set to True.

By setting to False, then optional types will always be treated as the wrapped type:

Disable Allow None Optionals#
from dataclasses import dataclass
from typing import Optional
from uuid import UUID

from polyfactory.factories.dataclass_factory import DataclassFactory


@dataclass
class Person:
    id: UUID
    name: Optional[str]


class PersonFactory(DataclassFactory[Person]):
    __allow_none_optionals__ = False


def test_optional_type_ignored() -> None:
    person_instance = PersonFactory.build()
    assert isinstance(person_instance.name, str)

Check Factory Fields#

When __check_model__ is set to True, declaring fields on the factory that don’t exist on the model will trigger an exception.

This is only true when fields are declared with Use, PostGenerated, Ignore and Require. Any other field definition will not be checked.

Enable Check Factory Fields#
from dataclasses import dataclass
from uuid import UUID

import pytest

from polyfactory import ConfigurationException, PostGenerated
from polyfactory.factories.dataclass_factory import DataclassFactory


@dataclass
class Person:
    id: UUID


def test_check_factory_fields() -> None:
    with pytest.raises(
        ConfigurationException,
        match="unknown_field is declared on the factory PersonFactory but it is not part of the model Person",
    ):

        class PersonFactory(DataclassFactory[Person]):
            __check_model__ = True
            unknown_field = PostGenerated(lambda: "foo")

Use Default Values#

If __use_defaults__ is set to True, then the default value will be used instead of creating a random value for a given field, provided there’s a default value for that field.

By default, __use_defaults__ is set to False. If you need more fine grained control, you can override the should_use_default_value() classmethod.

Note

Setting __use_defaults__ has no effect for TypedDictFactory since you cannot set default values for TypedDict.

Use Default Values#
from dataclasses import dataclass
from enum import Enum

from polyfactory.factories import DataclassFactory


class Species(str, Enum):
    CAT = "Cat"
    DOG = "Dog"


@dataclass
class Pet:
    name: str
    sound: str = "meow"
    species: Species = Species.CAT


class PetFactory(DataclassFactory[Pet]):
    __model__ = Pet
    __use_defaults__ = True


def test_use_default() -> None:
    pet = PetFactory.build()

    assert pet.species == Species.CAT
    assert pet.sound == "meow"