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.11.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
# 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
# Associations: assoc/4 accepts a prebuilt struct, params to build one from, or nothing
deffactory post(params \\ %{}), struct: Post do
base_params = %{title: FactoryMan.sequence("post", fn n -> "Post ##{n}" end)}
params = Map.put(params, :author, FactoryMan.assoc(params, :author, &build_user_struct/1, struct: User))
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

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", ...}
# 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:

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?

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.

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: