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 re-used.
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.