Model coverage generation¶
The BaseFactory.coverage()
function is an alternative approach to BaseFactory.batch()
, where the examples that are generated attempt to provide full coverage of all the forms a model can take with the minimum number of instances. For example:
from __future__ import annotations
from dataclasses import dataclass
from typing import Literal
from polyfactory.factories.dataclass_factory import DataclassFactory
@dataclass
class Car:
model: str
@dataclass
class Boat:
can_float: bool
@dataclass
class Profile:
age: int
favourite_color: Literal["red", "green", "blue"]
vehicle: Car | Boat
class ProfileFactory(DataclassFactory[Profile]): ...
def test_profile_coverage() -> None:
profiles = list(ProfileFactory.coverage())
assert profiles[0].favourite_color == "red"
assert isinstance(profiles[0].vehicle, Car)
assert profiles[1].favourite_color == "green"
assert isinstance(profiles[1].vehicle, Boat)
assert profiles[2].favourite_color == "blue"
assert isinstance(profiles[2].vehicle, Car)
As you can see in the above example, the Profile
model has 3 options for favourite_color
, and 2 options for vehicle
. In the output you can expect to see instances of Profile
that have each of these options. The largest variance dictates the length of the output, in this case favourite_color
has the most, at 3 options, so expect to see 3 Profile
instances.
Note
Notice that the same Car
instance is used in the first and final generated example. When the coverage examples for a field are exhausted before another field, values for that field are reused.
Notes on collection types¶
When generating coverage for models with fields that are collections, in particular collections that contain sub-models, the contents of the collection will be the all coverage examples for that sub-model. For example:
from __future__ import annotations
from dataclasses import dataclass
from typing import Literal
from polyfactory.factories.dataclass_factory import DataclassFactory
@dataclass
class Car:
model: str
@dataclass
class Boat:
can_float: bool
@dataclass
class Profile:
age: int
favourite_color: Literal["red", "green", "blue"]
vehicle: Car | Boat
@dataclass
class SocialGroup:
members: list[Profile]
class SocialGroupFactory(DataclassFactory[SocialGroup]): ...
def test_social_group_coverage() -> None:
groups = list(SocialGroupFactory.coverage())
assert len(groups) == 3
for group in groups:
assert len(group.members) == 1
assert groups[0].members[0].favourite_color == "red"
assert isinstance(groups[0].members[0].vehicle, Car)
assert groups[1].members[0].favourite_color == "green"
assert isinstance(groups[1].members[0].vehicle, Boat)
assert groups[2].members[0].favourite_color == "blue"
assert isinstance(groups[2].members[0].vehicle, Car)
Known Limitations¶
Recursive models will cause an error:
RecursionError: maximum recursion depth exceeded
.__min_collection_length__
and__max_collection_length__
are currently ignored in coverage generation.