FactoryMan
An Elixir library for generating test data. Define factories with deffactory, and FactoryMan
generates functions for building params, structs, and database records.
Inspired by ExMachina, but with a different API and feature set.
Looking for recipes? See the Cookbook.
Installation
Add FactoryMan to your mix.exs dependencies:
def deps do
[
{:factory_man, "~> 0.12.1", only: [:dev, :test]}
]
end
Then run mix deps.get.
Quick Tour
Define factories:
defmodule MyApp.Factory do
use FactoryMan, repo: MyApp.Repo
alias MyApp.Accounts.User
alias MyApp.Blog.{Post, Tag}
# Basic factory (struct-ful)
deffactory user(params \\ %{}), struct: User do
base_params = %{
username: FactoryMan.sequence("user"),
email: FactoryMan.sequence(:email, fn n -> "user#{n}@example.com" end),
role: FactoryMan.sequence(:role, ["admin", "mod", "user"]),
joined_at: fn -> DateTime.utc_now() end,
display: fn user -> "#{user.username} (#{user.role})" end
}
Map.merge(base_params, params)
end
# Variant: Preprocesses params, then delegates to the base factory
defvariant admin(params \\ %{}), for: :user do
base_params = %{role: "admin"}
Map.merge(base_params, params)
end
deffactory tag(params \\ %{}), struct: Tag do
base_params = %{name: FactoryMan.sequence("tag")}
Map.merge(base_params, params)
end
# Associations accept nested params through the declarative :associations option
deffactory post(params \\ %{}), struct: Post, associations: [author: :user, tags: :tag] do
base_params = %{
title: FactoryMan.sequence("post", fn n -> "Post ##{n}" end),
author: build_user_struct(%{username: "post-author"}),
tags: [
build_tag_struct(%{name: "elixir"}),
build_tag_struct(%{name: "testing"})
]
}
Map.merge(base_params, params)
end
# Struct-less factories: Only generates `build_*` and `build_*_list` functions
deffactory api_payload(params \\ %{}) do
base_params = %{action: "create", resource: "user"}
Map.merge(base_params, params)
end
end
Factory calls in base_params supply association defaults. The associations: option also lets
callers supply nested params or existing structs. An atom names a factory in the same module; use
{FactoryModule, :factory} for another module, as shown in the Cookbook.
Each factory generates a family of functions.
iex> alias MyApp.Factory, as: Factory
MyApp.Factory
# Build a struct without saving it
iex> Factory.build_user_struct(%{username: "alice"})
%User{id: nil, username: "alice", ...}
# Build associations from nested params
iex> Factory.build_post_struct(%{author: %{username: "alice"}, tags: [%{name: "elixir"}]})
%Post{author: %User{username: "alice", ...}, tags: [%Tag{name: "elixir"}], ...}
# Atom-keyed input for a changeset
iex> Factory.build_user_params(%{username: "alice"})
%{username: "alice", ...}
# String-keyed input for a controller test
iex> Factory.build_user_string_params(%{username: "alice"})
%{"username" => "alice", ...}
# Build several structs without saving them
iex> Factory.build_user_struct_list(3)
[%User{id: nil, ...}, %User{id: nil, ...}, %User{id: nil, ...}]
# Build and save a struct
iex> Factory.insert_user(%{username: "alice"})
%User{id: 1, username: "alice", ...}
# Build and insert multiple structs in a single call
iex> Factory.insert_user_list(3)
[%User{id: 2, ...}, %User{id: 3, ...}, %User{id: 4, ...}]
# Save an edited struct through the factory's insert hooks
iex> user = Factory.build_user_struct()
iex> Factory.insert_user_struct(%{user | username: "edited"})
%User{id: 5, username: "edited", ...}
# Build a variant struct
iex> Factory.build_admin_user_struct(%{username: "the_boss"})
%User{id: nil, role: "admin", username: "the_boss", ...}
# Insert a variant struct
iex> Factory.insert_admin_user(%{username: "the_boss"})
%User{id: 6, role: "admin", username: "the_boss", ...}
## Struct-less factory functions
# Build a struct-less factory item
iex> Factory.build_api_payload()
%{action: "create", resource: "user"}
# Build a list of struct-less factory items
iex> Factory.build_api_payload_list(2)
[%{action: "create", resource: "user"}, %{action: "create", resource: "user"}]
How It Works
You write one factory, and FactoryMan generates the rest:
build_<name>_struct— builds the struct in memory (runs your body, resolves lazy values, callsstruct!/2)build_<name>_params/build_<name>_string_params— builds the struct, then converts it to a clean params map (Ecto metadata stripped) for changesets or controller testsinsert_<name>— builds the struct and inserts it with your configured repoinsert_<name>_struct— inserts an already-built struct through the same insert pipeline- List builders —
build_<name>_struct_list,build_<name>_params_list, andbuild_<name>_string_params_listbuild each item independently.insert_<name>_listbuilds and inserts each item independently.insert_<name>_structhas no list counterpart.
Factories without a struct: option are simpler: they generate build_<name> and
build_<name>_list, and the body can return any value (maps, keyword lists, strings, ...).
Which Function Should I Use?
-
build_*_params— For testing changesets, passing to functions that expect maps, or when no struct shape is needed. -
build_*_struct— For setting association fields on other structs being built in memory. Use when the record doesn't need to exist in the database yet. -
insert_*— When a foreign key constraint requires the record to exist, or when the test queries the database for it.
Extending factories
Child factories with extends:
Use extends: to keep shared configuration, hooks, and helpers in a base factory while defining
factories in focused child modules:
defmodule MyApp.Factory do
use FactoryMan,
# These options will be inherited by any child factories that extend the parent
repo: MyApp.Repo,
hooks: [after_insert: &__MODULE__.unset_assocs/1]
@doc "Unset all assocs from a given Ecto schema `struct`."
def unset_assocs(struct) do
Ecto.reset_fields(struct, struct.__struct__.__schema__(:associations))
end
end
defmodule MyApp.Factory.Accounts do
# The child factory uses `:extends` to inherit options from the parent factory
use FactoryMan,
extends: MyApp.Factory,
# Child factories can add their own options too
repo: MyApp.OtherRepo
alias MyApp.Accounts.User
# This factory inherits the post-insert hook, so its assocs will be unset after insert
deffactory user(params \\ %{}), struct: User do
base_params = %{username: FactoryMan.sequence("user")}
Map.merge(base_params, params)
end
end
defmodule MyApp.Factory.Accounts.Admins do
use FactoryMan, extends: MyApp.Factory.Accounts
end
MyApp.Factory.Accounts overrides the parent's repo and still inherits its hooks and helpers.
MyApp.Factory.Accounts.Admins inherits those resolved options, including the repo override. Any
factory module can be extended again, so inheritance chains have no fixed depth.
Recommended project structure
Keep the base factory focused on shared config (repo, hooks, helpers). Child factories use
extends: to inherit that config, and mirror your application's context structure:
test/support/
factory.ex # Base factory (config, hooks, shared helpers)
factory/
accounts.ex # MyApp.Factory.Accounts (extends MyApp.Factory)
accounts/
admins.ex # MyApp.Factory.Accounts.Admins (extends MyApp.Factory.Accounts)
blog.ex # MyApp.Factory.Blog (extends MyApp.Factory)
blog/comments.ex # MyApp.Factory.Blog.Comments (extends MyApp.Factory)
Going Further
The full reference lives in the
FactoryMan module documentation, including:
- Hooks — transform data at each stage of the build/insert pipeline
- Variants (
defvariant) — lightweight presets that preprocess params for a base factory - Associations (
associations:) — normalize nested params through same- or cross-module factories - Strict params (
strict: true) — reject unknown param keys at the factory boundary - Sequences — counters, formatted values, and cycling lists
- Lazy evaluation — 0- and 1-arity functions as attribute values, resolved at build time
- Factory inheritance (
extends:) — share repo, hooks, and helper functions - Direct struct factories (
body: :struct) — full control over struct construction - Embedded schemas — build-only factories, detected automatically
- Cookbook — a practical progression from a first factory through realistic defaults, variants, associations, suite organization, hooks, strict params, and advanced presets