Generated functions

Struct factory (deffactory user(params \\ %{}), struct: User)

FunctionReturns
build_user_struct/0,1,2%User{}, not persisted
build_user_params/0,1,2Clean params map (atom keys)
build_user_string_params/0,1,2Clean params map (string keys)
insert_user/0,1,2Inserted %User{}
insert_user_struct/1,2Inserts an already-built struct
build_user_struct_list/1,2,3[%User{}, ...]
build_user_params_list/1,2,3[%{}, ...]
build_user_string_params_list/1,2,3[%{"" => ...}, ...]
insert_user_list/1,2,3[%User{}, ...], inserted

With the default insert: :ecto, insert functions need a repo and a table-backed Ecto schema. An insert: capture inserts any struct; insert: false removes them.

Insert targets (insert_via: [search: &M.index!/2])

FunctionReturns
insert_user_via_search/0,1,2%User{} from M.index!/2
insert_user_via_search_list/1,2,3[%User{}, ...]
insert_user_struct_via_search/1,2Inserts an already-built struct

Targets run no insert hooks and do not check for an already-inserted struct.

Disabling families (disable: [string_params: true])

KeyRemoves
:paramsbuild_*_params and its list
:string_paramsbuild_*_string_params and its list
:struct_listbuild_*_struct_list
:params_listbuild_*_params_list
:string_params_listbuild_*_string_params_list
:insert_listinsert_*_list, including target lists
:non_struct_listA non-struct factory's build_*_list

false enables an inherited family again. build_*_struct is always generated.

Non-struct factory (deffactory payload(params \\ %{}))

FunctionReturns
build_payload/0,1,2Whatever the body returns
build_payload_list/1,2,3A list of them

Variant (defvariant admin(...), for: :user)

FunctionNotes
build_admin_user_struct/0,1,2as: :boss renames to build_boss_struct
insert_admin_user/0,1,2Inserts through the base pipeline

Every function of the base factory has a variant counterpart. Without a body: defvariant admin, for: :user, defaults: %{role: "admin"} (or force:, or only extends:).

Which function?

NeedUse
A changeset or function that expects a mapbuild_*_params
Controller or JSON inputbuild_*_string_params
A struct to nest in another, no DB rowbuild_*_struct
A row a query, constraint, or FK needsinsert_*
Insert a struct you edited, with hooksinsert_*_struct

Options

use FactoryMan

OptionMeaning
:repoEcto repo for inserts
:extendsParent factory module to inherit from
:hooksHooks for every factory in the module
:strictDefault :strict for the module
:bodyDefault :body for the module
:insertDefault :insert for the module
:insert_viaInsert targets for the module
:disableFunction families to switch off

deffactory

OptionMeaning
:structStruct or Ecto schema to build
:assocsAssociation builders (Ecto schemas only)
:stricttrue, false, or [allow: [keys]]
:body:params (default) or :struct
:hooksChained with the module's hooks
:repoOverrides the module's repo
:insert:ecto (default), false, or &M.fun/2
:insert_vianame: &M.fun/2 adds or replaces, name: false removes
:disablefamily: true switches off, family: false switches back on

defvariant

OptionMeaning
:forThe base factory (required, must be a factory)
:extendsVariants of the same factory to build on
:asName for the generated functions
:assocsAssociation builders, resolved before the variant body
:defaultsWithout a body: a map the caller's params override
:forceWithout a body: a map that overrides the caller's params

Function options

FunctionOptions
build_*variants: [...] only
insert_*variants: [...]; every other option goes to the insert target
insert_*_structInsert target options only

Variants

Precedence (defaults form: Map.merge(defaults, params))

Wins overExample
Caller over variantsbuild_user_struct(%{role: "owner"}, variants: [:admin]) → "owner"
Later over earliervariants: [:admin, :moderator] → "moderator"
Variant over what it extendsextends: [:admin], whatever the list order

A variant written Map.merge(params, forced) forces its values: it wins over the caller and over every variant after it in the list.

Rules

RuleDetail
NamesList variants by their defvariant name (:admin)
Own functionsbuild_admin_user_struct(p, variants: [:x]) = build_user_struct(p, variants: [:admin, :x])
Execution orderFrom the last variant to the base factory
DuplicatesEach variant is applied once, at its first position
Definition orderA variant is defined after the variants it extends
Introspection__factory_man__(:variants, :user)

Strict params

strict: true checks

CheckWhenCatches
Unknown keysBefore hooks and the body%{usernme: "x"}
Ignored or changed paramsAfter the body and lazy valuesA body that forgets its merge

What the second check compares

Value givenKept when
Plain mapEach of its keys is kept (recursively), also when built into a struct
Anything elseThe built value is == to it
Function (lazy)Not checked
Association keyNot checked
Key in allow:Not checked, by either check

Hooks

Names and stages

HookReceivesRuns
:before_build_paramsparamsBefore assocs: and the body
:after_build_paramsparamsAfter the body and the strict check
:before_build_structparamsBefore struct!/2
:after_build_structstructAfter the struct is built
:before_insertstructBefore the default target's insert
:after_insertstructAfter the insert

body: :struct skips the three params-stage hooks. insert_via: targets skip the insert hooks.

Forms and placements

ValueMeaning
&M.hook/1One remote capture
[&M.a/1, &M.b/1]Several, run in order
{hooks, :before_parent}Before the inherited hooks (default for after_*)
{hooks, :after_parent}After the inherited hooks (default for before_*)
{hooks, :replace_parent}Instead of the inherited hooks
{[], :replace_parent}Switches off the inherited hooks

Associations (assocs:)

Builder forms

FormMeaning
&build_user_struct/1Builds an absent key with %{}
fn params, factory_params -> ... endAlso sees keys declared above it
{builder, default: nil}An absent key stays nil
{builder, default: [%{...}]}An absent list key is built from params
{builder, required: true}nil raises (singular keys only)

Caller values

Caller givesSingularPlural (list)
Key absentdefault: (%{})default: ([])
nilnilRaises (use [])
Params mapBuiltRaises
StructReusedRaises
ListRaisesEach item resolved

Sequences and reflection

Sequences

CallValues
FactoryMan.sequence("user")"user0", "user1", ...
FactoryMan.sequence(:email, &"u#{&1}@x.com")Formatted counter
FactoryMan.sequence(:role, ["a", "b"])Cycles through the list
FactoryMan.sequence(:n, &"#{&1}", start_at: 100)Starts at 100
FactoryMan.Sequence.reset(:email)Resets one (async-safe)
FactoryMan.Sequence.reset()Resets all, for every test

Reflection

CallReturns
__factory_man__(:opts)The module's resolved options
__factory_man__(:opts, :user)A factory's or variant's merged options
__factory_man__(:opts, :user)[:disable]The disabled families ([] by default)
__factory_man__(:factories)Factories and variants (full names)
__factory_man__(:variants, :user)A factory's variant names