Seeders & factories
A fresh database is empty. Seeders put data into it: reference data your application needs (countries, roles) or realistic sample data for development. Factories build model instances from a template of default attributes, so tests and seeders can create “a user” or “ten posts” in one line.
Factories
Section titled “Factories”A factory extends org.konaframework.cadence.factory.Factory<M> and implements definition(), which returns the default attributes for one model. The protected index property counts the models this factory has built (starting at 0); use it to make values unique.
package database.factories
import app.Models.Userimport org.konaframework.cadence.factory.Factory
class UserFactory : Factory<User>(User::class.java) { override fun definition(): Map<String, Any?> = mapOf( "name" to "User $index", "email" to "user$index@example.com", "admin" to false, )}package database.factories;
import app.Models.User;import org.konaframework.cadence.factory.Factory;import java.util.Map;
public class UserFactory extends Factory<User> { public UserFactory() { super(User.class); }
@Override public Map<String, Object> definition() { return Map.of( "name", "User " + getIndex(), "email", "user" + getIndex() + "@example.com", "admin", false); }}./grind make:factory UserFactory (or ./grind make:model User --factory) writes this skeleton under database/factories/.
Building models
Section titled “Building models”val user = UserFactory().make() // unsaved modelval saved = UserFactory().create() // saved modelval admin = UserFactory().create(mapOf("admin" to true)) // override attributesval many = UserFactory().count(10).createMany() // 10 saved modelsval drafts = UserFactory().count(3).makeMany() // 3 unsaved modelsval attributes = UserFactory().raw() // just the mapUser user = new UserFactory().make();User saved = new UserFactory().create();User admin = new UserFactory().create(Map.of("admin", true));List<User> many = new UserFactory().count(10).createMany();List<User> drafts = new UserFactory().count(3).makeMany();Map<String, Object> attributes = new UserFactory().raw();Factories set attributes with forceFill, so fillable and guarded do not apply: the factory is trusted code.
States, sequences, and callbacks
Section titled “States, sequences, and callbacks”The builder methods change the factory and return it, so you can chain them. They apply in this order: definition(), then each state in the order added, then the current sequence entry, then the overrides passed to make / create.
UserFactory() .state(mapOf("admin" to true)) // fixed overrides .state { attrs -> attrs + ("email" to attrs["email"].toString().uppercase()) } // computed .sequence(mapOf("team" to "red"), mapOf("team" to "blue")) // alternates per model .afterMaking { user -> user["nickname"] = user["name"] } .afterCreating { user -> user.posts().create(mapOf("title" to "Welcome")) } .count(4) .createMany()new UserFactory() .state(Map.of("admin", true)) .state(attrs -> { Map<String, Object> copy = new HashMap<>(attrs); copy.put("email", attrs.get("email").toString().toUpperCase()); return copy; }) .sequence(Map.of("team", "red"), Map.of("team", "blue")) .afterMaking(user -> user.setAttribute("nickname", user.getAttribute("name"))) .afterCreating(user -> user.posts().create(Map.of("title", "Welcome"))) .count(4) .createMany();Because builder calls change the factory, create a new factory for an unrelated batch rather than reusing a configured one.
Computed and related attributes
Section titled “Computed and related attributes”An attribute value may be:
- a
java.util.function.Supplier, called once per model; - another
Factory, whose model is created and whose key is used. This is how you give each post its own author:
class PostFactory : Factory<Post>(Post::class.java) { override fun definition(): Map<String, Any?> = mapOf( "title" to "Post $index", "user_id" to UserFactory(), // a new user per post "slug" to Supplier { "post-${System.nanoTime()}" }, )}Pass an explicit key in the overrides to reuse an existing model: PostFactory().count(3).createMany(mapOf("user_id" to ada.getKey())).
One-off factories
Section titled “One-off factories”Factory.of builds a factory from a lambda, which is handy in Java tests:
Factory<User> users = Factory.of(User.class, () -> Map.of("name", "Ada", "email", "ada@example.com"));users.count(2).createMany();Seeders
Section titled “Seeders”A seeder extends org.konaframework.cadence.seeding.Seeder and implements run(). A root seeder, conventionally database.seeders.DatabaseSeeder, calls the others with call so they run in a known order.
package database.seeders
import database.factories.UserFactoryimport org.konaframework.cadence.DBimport org.konaframework.cadence.seeding.Seeder
class RoleSeeder : Seeder() { override fun run() { DB.table("roles").insert(listOf(mapOf("name" to "admin"), mapOf("name" to "member"))) }}
class UserSeeder : Seeder() { override fun run() { UserFactory().count(20).createMany() }}
class DatabaseSeeder : Seeder() { override fun run() { call(RoleSeeder::class.java, UserSeeder::class.java) }}package database.seeders;
import database.factories.UserFactory;import org.konaframework.cadence.DB;import org.konaframework.cadence.seeding.Seeder;import java.util.List;import java.util.Map;
public class RoleSeeder extends Seeder { @Override public void run() { DB.table("roles").insert(List.of(Map.of("name", "admin"), Map.of("name", "member"))); }}
public class UserSeeder extends Seeder { @Override public void run() { new UserFactory().count(20).createMany(); }}
public class DatabaseSeeder extends Seeder { @Override public void run() { call(RoleSeeder.class, UserSeeder.class); }}call(SomeSeeder::class.java, …)builds each seeder through the container (the application’s container when run by grind, or a fresh one), so seeder constructors can ask for services. It then runs the seeders in order.call(SomeSeeder(), …)runs instances you created yourself.calledlists the simple names of every seeder run throughcall, nested calls included.Seeder.instantiate(type, container)builds a seeder the same waycalldoes, for running one from your own code.
Seeders and the factories they use write through the global database, so the database must be registered (with CadenceServiceProvider or Database.setAsGlobal()) before they run.
grind commands
Section titled “grind commands”./grind make:factory UserFactory # database/factories/UserFactory./grind make:factory CrewFactory --model=Person # factory for app.Models.Person./grind make:seeder UserSeeder # database/seeders/UserSeeder./grind make:model User --factory --seeder # model plus both./grind make:model User --all # model, migration, factory, seeder
./grind db:seed # runs database.seeders.DatabaseSeeder./grind db:seed --class=database.seeders.UserSeederdb:seed boots the application in-process from its compiled classes (run ./gradlew classes first), makes its database global, resolves the seeder through the application container, runs it, and prints each seeder it ran. It fails with a message when the class is missing or does not extend Seeder.
New applications created by kona new compile the database/ directory along with app/, so generated migrations, factories, and seeders are part of the build.