Declaring Factories¶
Defining factories is done using a declarative syntax. That is - users declare factory classes, which are subclasses of base factories.
from dataclasses import dataclass
from polyfactory.factories import DataclassFactory
@dataclass
class Person:
name: str
age: float
height: float
weight: float
class PersonFactory(DataclassFactory[Person]):
__model__ = Person
def test_is_person() -> None:
person_instance = PersonFactory.build()
assert isinstance(person_instance, Person)
assert isinstance(person_instance.name, str)
assert isinstance(person_instance.age, float)
assert isinstance(person_instance.height, float)
assert isinstance(person_instance.weight, float)
You can also specify the model type by only specifying the factory generic type parameter.
@dataclass
class Person:
name: str
age: float
height: float
weight: float
class PersonFactory(DataclassFactory[Person]):
...
Note
The syntax with the __model__
class attribute omitting
is only available since version 2.13.0.
The same applies to the other factories exported by this library, for example:
from typing import TypedDict
from polyfactory.factories import TypedDictFactory
class Person(TypedDict):
name: str
age: float
height: float
weight: float
class PersonFactory(TypedDictFactory[Person]): ...
def test_is_person() -> None:
person_instance = PersonFactory.build()
assert isinstance(person_instance, dict)
assert isinstance(person_instance.get("name"), str)
assert isinstance(person_instance.get("age"), float)
assert isinstance(person_instance.get("height"), float)
assert isinstance(person_instance.get("weight"), float)
Or for pydantic models:
from pydantic import BaseModel
from polyfactory.factories.pydantic_factory import ModelFactory
class Person(BaseModel):
name: str
age: float
height: float
weight: float
class PersonFactory(ModelFactory[Person]): ...
def test_is_person() -> None:
person_instance = PersonFactory.build()
assert isinstance(person_instance, Person)
assert isinstance(person_instance.name, str)
assert isinstance(person_instance.age, float)
assert isinstance(person_instance.height, float)
assert isinstance(person_instance.weight, float)
Note
You can also define factories for any 3rd party implementation of dataclasses, as long as it fulfills the stdlib
dataclasses interface. For example, this is using the pydantic @dataclass
decorator:
from pydantic.dataclasses import dataclass
from polyfactory.factories import DataclassFactory
@dataclass
class Person:
name: str
age: float
height: float
weight: float
class PersonFactory(DataclassFactory[Person]): ...
def test_is_person() -> None:
person_instance = PersonFactory.build()
assert isinstance(person_instance, Person)
assert isinstance(person_instance.name, str)
assert isinstance(person_instance.age, float)
assert isinstance(person_instance.height, float)
assert isinstance(person_instance.weight, float)
Or for attrs models:
from datetime import date, datetime
from typing import Any, Dict, List, Union
from uuid import UUID
import attrs
from polyfactory.factories.attrs_factory import AttrsFactory
@attrs.define
class Person:
id: UUID
name: str
hobbies: List[str]
age: Union[float, int]
# an aliased variable
birthday: Union[datetime, date] = attrs.field(alias="date_of_birth")
# a "private" variable
_assets: List[Dict[str, Dict[str, Any]]]
class PersonFactory(AttrsFactory[Person]): ...
def test_person_factory() -> None:
person = PersonFactory.build()
assert isinstance(person, Person)
Note
Validators are not currently supported - neither the built in validators that come with attrs nor custom validators.
Imperative Factory Creation¶
Although the definition of factories is primarily meant to be done declaratively, factories expose the
create_factory
method. This method is used internally
inside factories to dynamically create factories for models. For example, below the PersonFactory
will dynamically
create a PetFactory
:
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 use this method to create factories imperatively:
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
species: Species
sound: str
def test_imperative_factory_creation() -> None:
pet_factory = DataclassFactory.create_factory(model=Pet)
pet_instance = pet_factory.build()
assert isinstance(pet_instance, Pet)
Eventually you can use this method on an existing concrete factory to create a sub factory overriding some parent configuration:
from dataclasses import dataclass
from enum import Enum
from polyfactory.factories import DataclassFactory
class Species(str, Enum):
CAT = "Cat"
DOG = "Dog"
RABBIT = "Rabbit"
MOUSE = "Mouse"
@dataclass
class Pet:
name: str
species: Species
sound: str
def test_imperative_sub_factory_creation() -> None:
pet_factory = DataclassFactory.create_factory(model=Pet)
cat_factory = pet_factory.create_factory(species=Species.CAT)
cat_instance = cat_factory.build()
assert isinstance(cat_instance, Pet)
assert cat_instance.species == Species.CAT
In this case you don’t need to specify the model argument to the create_factory
method. The one from the parent factory will be used.