Skip to content

Factories

A factory makes objects of a model for tests — each field from a declaration, a value given to the call winning over it. The model is the factory's type argument:

from hare.contrib.factories import LazyAttribute, ManyToMany, ModelFactory, RelatedFactory, Sequence, SubFactory, Trait


class PublisherFactory(ModelFactory[Publisher]):
    name = Sequence(lambda number: f"publisher{number}")


class AuthorFactory(ModelFactory[Author]):
    name = Sequence(lambda number: f"author{number}")
    email = LazyAttribute(lambda author: f"{author.name}@{author.domain}")
    publisher = SubFactory(PublisherFactory)
    tags = ManyToMany(TagFactory, size=2)
    books = RelatedFactory("library.factories.BookFactory", "author", size=3)

    class Params:
        domain = "example.com"
        featured = Trait(is_featured=True)


author = await AuthorFactory.create(name="ann")        # saved, with its publisher, tags and books
featured = await AuthorFactory.create(featured=True)   # the trait's values
draft = AuthorFactory.build(publisher=publisher)       # not saved; a related object only given
authors = await AuthorFactory.create_batch(10)
drafts = AuthorFactory.build_batch(3)

Declarations

Declaration The value of each object
a plain value The value itself — a list, a dict or a set copied for each object, so a change to one object's leaves the others'.
Sequence(function) function(number) — the object's number in its factory's sequence, from 0 (reset_sequence() starts it again). A factory subclassing another one of the same model numbers its objects on in that sequence — they are rows of one table.
LazyFunction(function) function().
LazyAttribute(function) function(values) — the object's other values as attributes, the parameters of Params among them. Made after every other value, in the order declared.
Iterator(values, *, cycle=True) The next of the values, from the start again once they end; with cycle=False an object made after the last value raises ConfigurationError.
Faker(provider, *, locale=None, **arguments) A value of the faker library's provider (pip install faker; without it ConfigurationError).
SubFactory(factory, **values) An object of another factory, created with the object — a built object gets none: a model takes only a saved related object, given to build(). The target of a GenericForeignKeyField too.
RelatedFactory(factory, related_name, *, size=1, **values) After the object is created, size rows of another factory whose related_name points at it; a number given under the declaration's name replaces size.
ManyToMany(factory=None, *, size=0, **values) After the object is created, links to size objects of another factory; objects given under its name are linked instead.
Trait(**values) in Params The values, when the parameter is given true.

A declaration of your own that acts once the object is created derives from PostDeclaration (hare.contrib.factories) and implements async def apply(self, obj, name, given) -> None — name is its name on the factory, given what create() got under that name (None for nothing). A built object, never saved, gets none of it.

Naming, inheritance and tenants

A factory may be named by its dotted path, for two factories naming each other. A factory inherits the declarations of the factory it subclasses. A model's Meta.tenant_field gets the tenant of the active Tenancy.scope() when not given; a composite primary key is declared field by field.

Batches

create_batch() inserts the objects with one bulk_create() when none of them makes a related object or acts once created (SubFactory, RelatedFactory, ManyToMany) and the database gives back the primary keys it generates in insert order (RETURNING, PostgreSQL), else creates them one by one — every object it returns has its primary key. build_batch() builds unsaved objects the same way as build(). A built object gets no RelatedFactory/ManyToMany — it isn't saved.