Skip to content
Back to skills

Polyfactory

ASecurity

Auto-activate for polyfactory, ModelFactory, DataclassFactory, MsgspecFactory, AttrsFactory, Use, register_fixture, pytest plugin, __random_seed__, or coverage(). Not for production seeding.

  • 15 stars
  • 0 votes
  • 0 copies
  • 2 views
  • Added May 28, 2026
ai-agentspythonsqlrailstestinggitbackend

Works with

  • cli

Security analysis

A100/100

Pro scans all 4 files and shows the line behind each finding

Scanned October 5, 2026

npx -y skills add litestar-org/litestar-skills --skill polyfactory --agent claude-code

Installs into .claude/skills of the current project.

Are you the author of Polyfactory?

Add the live security badge to your README. It updates with every re-scan.

Security grade badge for Polyfactory
[![Security: A — Skills Directory](https://www.skillsdirectory.com/api/skills/litestar-org-polyfactory/badge)](https://www.skillsdirectory.com/skills/litestar-org-polyfactory)

More formats (shields.io, HTML) on the badges page. Keep it an A: scan every change in CI with Pro.

Download with Pro
SKILL.md
---
name: polyfactory
description: "Auto-activate for polyfactory, ModelFactory, DataclassFactory, MsgspecFactory, AttrsFactory, Use, register_fixture, pytest plugin, __random_seed__, or coverage(). Not for production seeding."
---

# polyfactory

Polyfactory is a typed mock-data factory library: declare `ModelFactory[T]` (or `DataclassFactory`, `MsgspecFactory`, `AttrsFactory`, `TypedDictFactory`) and `.build()` returns a populated instance of `T`. The matching factory base inspects the model's annotations and supported constraints so tests do not need hand-written happy-path fixtures.

In Litestar projects, polyfactory's pytest plugin is the canonical way to feed `TestClient.post(...)` / `AsyncTestClient.put(...)` payloads. The companion skill `litestar:litestar-testing` covers the request side.

## Code Style Rules

- PEP 604 unions: `T | None`, never `Optional[T]`.
- One factory per model. Don't reuse a factory across unrelated models — clarity beats DRY when the test fails at 3am.
- Pick the right factory base by backend: `ModelFactory` (Pydantic), `DataclassFactory`, `MsgspecFactory`, `AttrsFactory`, `TypedDictFactory`. A mismatched base raises `ConfigurationException`.
- Prefer `register_fixture` over hand-rolled `@pytest.fixture` wrappers. Call it in a pytest-discovered test module or `conftest.py` so the injected fixture is collected.

## Quick Reference

### Picking the right factory base

| Model kind | Factory base | Import |
| --- | --- | --- |
| `pydantic.BaseModel` | `ModelFactory` | `from polyfactory.factories.pydantic_factory import ModelFactory` |
| `@dataclass` | `DataclassFactory` | `from polyfactory.factories.dataclass_factory import DataclassFactory` |
| `msgspec.Struct` | `MsgspecFactory` | `from polyfactory.factories.msgspec_factory import MsgspecFactory` |
| `@attrs.define` / `attr.s` | `AttrsFactory` | `from polyfactory.factories.attrs_factory import AttrsFactory` |
| `TypedDict` | `TypedDictFactory` | `from polyfactory.factories.typed_dict_factory import TypedDictFactory` |
| SQLAlchemy declarative | `SQLAlchemyFactory` | `from polyfactory.factories.sqlalchemy_factory import SQLAlchemyFactory` |
| Beanie `Document` | `BeanieDocumentFactory` | `from polyfactory.factories.beanie_odm_factory import BeanieDocumentFactory` |
| Odmantic `Model` / `EmbeddedModel` | `OdmanticModelFactory` | `from polyfactory.factories.odmantic_odm_factory import OdmanticModelFactory` |

`polyfactory.factories.__all__` only exports `BaseFactory`, `DataclassFactory`, and `TypedDictFactory`. Always import backend-specific factories (`ModelFactory`, `MsgspecFactory`, `AttrsFactory`, `SQLAlchemyFactory`, `BeanieDocumentFactory`, `OdmanticModelFactory`) from their concrete submodules (`polyfactory.factories.pydantic_factory`, `polyfactory.factories.msgspec_factory`, `polyfactory.factories.sqlalchemy_factory`, etc.), `register_fixture` from `polyfactory.pytest_plugin`, and `post_generated` from `polyfactory.decorators` so runtime imports and `mypy`/`pyright` checks succeed.

### Defining a factory

```python
from dataclasses import dataclass
from polyfactory.factories.dataclass_factory import DataclassFactory


@dataclass
class Order:
    id: int
    customer_email: str
    total_cents: int
    status: str


class OrderFactory(DataclassFactory[Order]):
    pass


# Use it
one = OrderFactory.build()
many = OrderFactory.batch(10)
```

The single concrete generic argument lets Polyfactory infer `__model__`. `build()` returns one instance, `batch(n)` returns `list[T]`, `coverage()` yields the smallest set of instances that covers the model's supported variants, `create_sync()` / `create_batch_sync(n)` and `create_async()` / `create_batch_async(n)` build and persist instances, `seed_random(seed)` re-seeds the factory PRNG and Faker instance, and `add_provider(type_, fn)` registers a custom type generator.

### Customizing fields

```python
from polyfactory import Ignore, Require, Use
from polyfactory.decorators import post_generated
from polyfactory.factories import DataclassFactory


class OrderFactory(DataclassFactory[Order]):
    # Plain literal — every build returns this exact value
    status = "pending"

    # Callable — re-evaluated per build
    customer_email = Use(lambda: "test@example.com")

    # Random choice — re-evaluated per build
    total_cents = Use(DataclassFactory.__random__.randint, 100, 10_000)

    # Skip generation so the model's default / default_factory runs
    internal_note = Ignore()

    # Force callers to supply this kwarg on build() / batch() / coverage()
    tenant_id = Require()

    # Derive a field from already-generated fields on the same instance
    @post_generated
    @classmethod
    def reference(cls, id: int) -> str:
        return f"order-{id}"
```

`Use(callable, *args, **kwargs)` is re-invoked on every `build()`, so each generated instance gets a fresh value. `Ignore()` and `Require()` must be instantiated; omitting a `Require()` field at build time raises `MissingBuildKwargException`.

For standalone post-generation callbacks, use `PostGenerated(fn, *args, **kwargs)` where `fn` has signature `(field_name, generated_values, *args, **kwargs)`:

```python
from typing import Any

from polyfactory import PostGenerated


def order_reference(name: str, values: dict[str, Any], prefix: str) -> str:
    return f"{prefix}-{values['id']}"


class OrderFactory(DataclassFactory[Order]):
    reference = PostGenerated(order_reference, "order")
```

### Determinism and configuration

```python
class OrderFactory(DataclassFactory[Order]):
    __random_seed__ = 42  # seeds cls.__random__ and cls.__faker__ at class creation


OrderFactory.seed_random(42)  # re-seed at runtime before a test or build
```

Set `__random_seed__` (or call `Factory.seed_random(seed)` / `__faker__.seed_instance(seed)` on a custom `__faker__ = Faker(locale="en_US")`) when test assertions depend on the exact generated values. Never pass `Faker(seed=...)` — `Faker.__init__` ignores a `seed` keyword argument without seeding.

Core `BaseFactory` configuration attributes:

- `__model__`: target model class (inferred from a single concrete generic argument when omitted).
- `__check_model__ = True`: validates that `Use`, `PostGenerated`, `Ignore`, and `Require` attribute names exist on `__model__`.
- `__random_seed__`: integer seed passed to `cls.seed_random(...)` during class creation.
- `__use_defaults__ = False`: when `True`, prefers model field defaults / `default_factory` over generated values.
- `__allow_none_optionals__ = True`: when `True`, optional fields (`T | None`) randomly produce `None`; set `False` to always populate `T`.
- `__set_as_default_factory_for_type__ = False`: when `True`, registers the factory globally for nested fields of type `__model__`.
- `__faker__` and `__random__`: the factory's `Faker` and `random.Random` instances.

### Default factory registration

```python
class CustomerFactory(DataclassFactory[Customer]):
    __set_as_default_factory_for_type__ = True


@dataclass
class Order:
    id: int
    customer: Customer  # automatically populated by CustomerFactory.build()


class OrderFactory(DataclassFactory[Order]):
    pass
```

When `__set_as_default_factory_for_type__ = True`, polyfactory uses that factory whenever the type appears as a field on another model — no manual nesting required.

### Pytest fixture from a factory

```python
from polyfactory.pytest_plugin import register_fixture
from polyfactory.factories.dataclass_factory import DataclassFactory


@register_fixture
class OrderFactory(DataclassFactory[Order]):
    pass


def test_order_total(order_factory: type[OrderFactory]) -> None:
    order = order_factory.build()
    assert order.total_cents >= 0
```

`@register_fixture` returns the class unchanged and injects a pytest fixture into the caller's module. The fixture name is the snake-cased class name and its value is the factory class.

### Cross-model relationships

```python
from polyfactory import Use
from polyfactory.pytest_plugin import register_fixture


@register_fixture
class CustomerFactory(DataclassFactory[Customer]):
    __set_as_default_factory_for_type__ = True


@register_fixture
class OrderFactory(DataclassFactory[Order]):
    customer = Use(CustomerFactory.build)  # explicit local override
```

`__set_as_default_factory_for_type__ = True` lets Polyfactory populate nested `Customer` fields with `CustomerFactory`. Use `Use(CustomerFactory.build)` when one parent factory needs an explicit local override.

<workflow>

## Workflow

### Step 1: Pick the factory base

Match the base to your model backend (table above). A mismatched base fails during factory class creation. If your project uses multiple backends (e.g., Pydantic for HTTP DTOs + msgspec for internal events), import each base separately and don't try to share a factory across backends.

### Step 2: Define one factory per model

Subclass the appropriate base with one concrete generic argument and let Polyfactory infer `__model__`. Set `__model__` explicitly only when the generic declaration cannot identify exactly one model. Keep factories in `tests/factories.py` or `tests/<feature>/factories.py`, outside production code paths.

### Step 3: Customize only what the test cares about

If a field can take any valid value, leave it for the factory to randomize. Override (literal value or `Use(...)`) only fields the assertion depends on. Tests that pin every field defeat the purpose of using a factory.

### Step 4: Register as a pytest fixture (if used widely)

For factories used in many tests, decorate with `@register_fixture` in a pytest-discovered module and consume the snake-cased fixture name. For one-off use, call `Factory.build()` directly inline.

### Step 5: Wire cross-model relationships

Set `__set_as_default_factory_for_type__ = True` on a base factory and let nested fields be populated automatically, or use `Use(OtherFactory.build)` for a local relationship override.

### Step 6: Pin determinism only when needed

Tests that assert on specific generated values need `__random_seed__` (or `Factory.seed_random(seed)` per test). Tests that assert on shape or invariants (e.g., `total >= 0`) should not — leaving randomization on widens coverage across runs.

</workflow>

<guardrails>

## Guardrails

- **Import from concrete submodules for `mypy`/`pyright` and runtime safety.** `polyfactory.factories.__all__` only exports `BaseFactory`, `DataclassFactory`, and `TypedDictFactory`. Import `ModelFactory` from `polyfactory.factories.pydantic_factory`, `MsgspecFactory` from `polyfactory.factories.msgspec_factory`, `SQLAlchemyFactory` from `polyfactory.factories.sqlalchemy_factory`, `register_fixture` from `polyfactory.pytest_plugin`, and `post_generated` from `polyfactory.decorators`.
- **Use one concrete generic argument or set `__model__`.** Polyfactory infers `__model__` from `DataclassFactory[Order]`; an unparameterized concrete factory without `__model__` raises `ConfigurationException` during class creation.
- **Don't override fields you're about to assert on with random values.** Either pin the value (`status = "pending"`) or assert on shape, not both.
- **Don't reuse `__random_seed__` across factories that share a Faker instance.** They will collide and produce unexpected duplicates. Use a different seed per factory or a single shared seeded `__faker__` (via `__faker__.seed_instance(seed)`).
- **Use the right base for the backend.** `ModelFactory` on a `msgspec.Struct` raises `ConfigurationException`; use `MsgspecFactory`.
- **Factories belong under `tests/`.** Importing them from production modules ties test data to runtime code and is a refactor hazard.
- **`coverage()` is not a Cartesian-product generator.** It emits a minimal representative set and reuses exhausted field variants; use Hypothesis for exhaustive input-space exploration.
- **`__allow_none_optionals__` is boolean.** `True` allows random `None` values during `build()`; `False` always generates the wrapped type. It is not a probability.
- **Instantiate `Ignore()` and `Require()`, and place `@post_generated` above `@classmethod`.** Because `Ignore` and `Require` are classes, assigning `field = Ignore` causes `build()` to invoke `Ignore()` as a callable and assign the marker object as the field value; stacking `@classmethod` above `@post_generated` raises `TypeError` at decoration time.
- **Async methods persist data.** `create_async()` and `create_batch_async()` require `__async_persistence__` or a backend factory that supplies it. Polyfactory has no `build_async()`.

</guardrails>

<validation>

## Validation Checkpoint

Before delivering polyfactory code, verify:

- [ ] Factory base matches the model backend (Pydantic → `ModelFactory`, dataclass → `DataclassFactory`, msgspec → `MsgspecFactory`, attrs → `AttrsFactory`).
- [ ] Each factory has one concrete generic model argument or an explicit `__model__`.
- [ ] Factories live under `tests/` (or a sibling test-only module), never in production code.
- [ ] Fields overridden in the factory match what the test asserts on; fields the test does not care about are left for randomization.
- [ ] If the test asserts on exact values, `__random_seed__` is set; otherwise it is not.
- [ ] `@register_fixture` is used for factories shared across more than ~2 test files; one-offs call `.build()` inline.
- [ ] Cross-model relationships use `__set_as_default_factory_for_type__` or `Use(OtherFactory.build)`.
- [ ] `register_fixture` is invoked in a pytest-discovered module.
- [ ] Async factory calls use `create_async()` / `create_batch_async()` only when persistence is configured.

</validation>

<example>

## Example: Litestar handler test with msgspec DTOs and polyfactory

```python
# tests/factories.py
from polyfactory.factories.msgspec_factory import MsgspecFactory

from myapp.events import OrderCreatedEvent  # msgspec.Struct


class OrderCreatedEventFactory(MsgspecFactory[OrderCreatedEvent]):
    pass
```

```python
# tests/conftest.py
from polyfactory.pytest_plugin import register_fixture

from tests.factories import OrderCreatedEventFactory


register_fixture(OrderCreatedEventFactory)
```

```python
# tests/test_orders.py
from __future__ import annotations  # consumer module — fine to use future annotations

import msgspec
import pytest
from litestar.testing import AsyncTestClient

from tests.factories import OrderCreatedEventFactory


@pytest.mark.anyio
async def test_create_order_emits_event(
    client: AsyncTestClient,
    order_created_event_factory: type[OrderCreatedEventFactory],
) -> None:
    payload = order_created_event_factory.build()
    response = await client.post("/orders", json=msgspec.to_builtins(payload))

    assert response.status_code == 201
    assert response.json()["id"] == payload.id
```

The factory provides a fully-populated, validation-passing `OrderCreatedEvent`; the test focuses on the request/response contract instead of constructing fake data inline.

</example>

---

## References Index

For detailed guides, refer to the following documents in `references/`:

- **[Factories](references/factories.md)** — Per-backend factory bases, model inference, randomization control, `PostGenerated`, model coverage, persistence protocols, and SQLAlchemy 3.3 persistence behavior.
- **[Pytest integration](references/pytest-integration.md)** — `@register_fixture`, separate registration from `conftest.py`, fixture scoping and naming, collection boundaries, and async persistence.
- **[Litestar patterns](references/litestar-patterns.md)** — Using factories with `TestClient` / `AsyncTestClient`, parametrizing handler tests via `coverage()`, integrating with `litestar-testing` fixtures, msgspec DTOs, advanced-alchemy model factories, and SAQ task payload generation.

---

## Official References

- <https://github.com/litestar-org/polyfactory/tree/v3.3.0/polyfactory> — immutable v3.3.0 source
- <https://github.com/litestar-org/polyfactory/tree/v3.3.0/tests> — immutable v3.3.0 contract tests
- <https://github.com/litestar-org/polyfactory/releases/tag/v3.3.0> — v3.3.0 release
- <https://polyfactory.litestar.dev/>
- <https://polyfactory.litestar.dev/usage/index.html>
- <https://polyfactory.litestar.dev/usage/library_factories/index.html>
- <https://polyfactory.litestar.dev/usage/decorators.html>
- <https://polyfactory.litestar.dev/usage/fixtures.html>
- <https://polyfactory.litestar.dev/usage/configuration.html>
- <https://github.com/litestar-org/polyfactory>

## Shared Styleguide Baseline

- [General Principles](../litestar-styleguide/references/general.md)
- [Python](../litestar-styleguide/references/python.md)

Files in this skill

  • SKILL.md12.4 KB
  • references/factories.md6.8 KB
  • references/litestar-patterns.md6.3 KB
  • references/pytest-integration.md5.3 KB

Attribution

Is this your skill, or is something wrong with this listing? Request removal or report an issue. Author removals are honored within 72 hours.

Comments

Loading comments…

Newsletter

Get the weekly list of safe new skills, and the ones we pulled.