Factories

Factories require the Jaker optional dependency (com.github.EzFramework:jaker:1.0.0). See Installation.

Table of contents

  1. Overview
  2. Creating a factory
  3. Opting in — HasFactory
  4. Building instances
    1. make() — transient (not persisted)
    2. make(n) — multiple transient instances
    3. create(repo) — persisted
    4. create(n, repo) — multiple persisted
  5. Overriding attributes with state
  6. Batch operations with count
  7. Custom Faker locale
  8. Factory method reference

Overview

Jaloquent ships a Laravel-style factory system built on Jaker, a Java fake-data library. Factories let you generate realistic model instances for tests and seeders with minimal boilerplate.

Factory<T>          ← abstract base — you implement definition()
  └── PlayerFactory ← your concrete factory
        ↓
  factory().make()              → transient Player
  factory().create(repo)        → persisted Player
  factory().count(5).create(repo) → List<Player> (persisted)

Creating a factory

Extend Factory<T> and implement definition(Faker):

import com.github.ezframework.jaloquent.model.Factory;
import com.github.ezframework.jaker.Faker;

public class PlayerFactory extends Factory<Player> {

    @Override
    protected Map<String, Object> definition(Faker faker) {
        return Map.of(
            "name",   faker.name().firstName(),
            "coins",  faker.number().numberBetween(0, 10_000),
            "status", "active",
            "level",  1
        );
    }
}

The naming convention is <ModelName>Factory — this is required by Factory.discover().


Opting in — HasFactory

Add the HasFactory marker interface to your model and a static factory() method:

import com.github.ezframework.jaloquent.model.HasFactory;
import com.github.ezframework.jaloquent.model.Factory;

public class Player extends Model implements HasFactory {

    public Player(String id) { super(id); }

    public static PlayerFactory factory() {
        return Factory.discover(Player.class);
    }

    // ... typed accessors ...
}

Factory.discover(Player.class) locates PlayerFactory by convention and returns a new instance. It throws IllegalStateException if no matching factory class is found.


Building instances

make() — transient (not persisted)

Player p = Player.factory().make();
// p.getId() is set to a random UUID; no repo call is made

make(n) — multiple transient instances

List<Player> players = Player.factory().make(5);

create(repo) — persisted

Player p = Player.factory().create(playerRepo);
// model is built, then repo.save() is called

create(n, repo) — multiple persisted

List<Player> players = Player.factory().create(10, playerRepo);

Overriding attributes with state

state(Map) merges overrides on top of definition(). Later state() calls win. Returns this for chaining.

// Single override
Player admin = Player.factory()
    .state(Map.of("role", "admin", "level", 99))
    .make();

// Chained overrides
Player banned = Player.factory()
    .state(Map.of("status", "banned"))
    .state(Map.of("coins", 0))
    .make();

Batch operations with count

factory().count(n) returns a FactoryCount<T> that always produces a List<T>:

// Transient list
List<Player> players = Player.factory().count(5).make();

// Persisted list
List<Player> players = Player.factory().count(5).create(playerRepo);

// With state applied to all instances in the batch
List<Player> vips = Player.factory()
    .state(Map.of("status", "vip"))
    .count(3)
    .create(playerRepo);

Custom Faker locale

By default, factories use en-US locale. Pass a custom Faker instance:

import com.github.ezframework.jaker.Faker;

Faker deFaker = new Faker(Locale.GERMAN);
PlayerFactory factory = new PlayerFactory(deFaker);
Player player = factory.make();

Or specify the model class explicitly:

PlayerFactory factory = new PlayerFactory(deFaker, Player.class);

Factory method reference

Method Returns Description
definition(Faker) Map<String, Object> Abstract — return fake attributes for one instance
state(Map) Factory<T> Merge attribute overrides; chainable
count(int) FactoryCount<T> Switch to batch mode
make() T Build one transient model
make(int) List<T> Build N transient models
create(ModelRepository<T>) T Build + persist one model
create(int, ModelRepository<T>) List<T> Build + persist N models
Factory.discover(Class<M>) Factory<M> Locate and instantiate MFactory by convention