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:

Defining a factory and generating examples with coverage
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:

Coverage output for the SocialGroup model
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.