Generated functions
Struct factory (deffactory user(params \\ %{}), struct: User)
| Function | Returns |
|---|---|
build_user_struct/0,1,2 | %User{}, not persisted |
build_user_params/0,1,2 | Clean params map (atom keys) |
build_user_string_params/0,1,2 | Clean params map (string keys) |
insert_user/0,1,2 | Inserted %User{} |
insert_user_struct/1,2 | Inserts 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])
| Function | Returns |
|---|---|
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,2 | Inserts an already-built struct |
Targets run no insert hooks and do not check for an already-inserted struct.
Disabling families (disable: [string_params: true])
| Key | Removes |
|---|---|
:params | build_*_params and its list |
:string_params | build_*_string_params and its list |
:struct_list | build_*_struct_list |
:params_list | build_*_params_list |
:string_params_list | build_*_string_params_list |
:insert_list | insert_*_list, including target lists |
:non_struct_list | A non-struct factory's build_*_list |
false enables an inherited family again. build_*_struct is always generated.
Non-struct factory (deffactory payload(params \\ %{}))
| Function | Returns |
|---|---|
build_payload/0,1,2 | Whatever the body returns |
build_payload_list/1,2,3 | A list of them |
Variant (defvariant admin(...), for: :user)
| Function | Notes |
|---|---|
build_admin_user_struct/0,1,2 | as: :boss renames to build_boss_struct |
insert_admin_user/0,1,2 | Inserts 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?
| Need | Use |
|---|---|
| A changeset or function that expects a map | build_*_params |
| Controller or JSON input | build_*_string_params |
| A struct to nest in another, no DB row | build_*_struct |
| A row a query, constraint, or FK needs | insert_* |
| Insert a struct you edited, with hooks | insert_*_struct |
Options
use FactoryMan
| Option | Meaning |
|---|---|
:repo | Ecto repo for inserts |
:extends | Parent factory module to inherit from |
:hooks | Hooks for every factory in the module |
:strict | Default :strict for the module |
:body | Default :body for the module |
:insert | Default :insert for the module |
:insert_via | Insert targets for the module |
:disable | Function families to switch off |
deffactory
| Option | Meaning |
|---|---|
:struct | Struct or Ecto schema to build |
:assocs | Association builders (Ecto schemas only) |
:strict | true, false, or [allow: [keys]] |
:body | :params (default) or :struct |
:hooks | Chained with the module's hooks |
:repo | Overrides the module's repo |
:insert | :ecto (default), false, or &M.fun/2 |
:insert_via | name: &M.fun/2 adds or replaces, name: false removes |
:disable | family: true switches off, family: false switches back on |
defvariant
| Option | Meaning |
|---|---|
:for | The base factory (required, must be a factory) |
:extends | Variants of the same factory to build on |
:as | Name for the generated functions |
:assocs | Association builders, resolved before the variant body |
:defaults | Without a body: a map the caller's params override |
:force | Without a body: a map that overrides the caller's params |
Function options
| Function | Options |
|---|---|
build_* | variants: [...] only |
insert_* | variants: [...]; every other option goes to the insert target |
insert_*_struct | Insert target options only |
Variants
Precedence (defaults form: Map.merge(defaults, params))
| Wins over | Example |
|---|---|
| Caller over variants | build_user_struct(%{role: "owner"}, variants: [:admin]) → "owner" |
| Later over earlier | variants: [:admin, :moderator] → "moderator" |
| Variant over what it extends | extends: [: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
| Rule | Detail |
|---|---|
| Names | List variants by their defvariant name (:admin) |
| Own functions | build_admin_user_struct(p, variants: [:x]) = build_user_struct(p, variants: [:admin, :x]) |
| Execution order | From the last variant to the base factory |
| Duplicates | Each variant is applied once, at its first position |
| Definition order | A variant is defined after the variants it extends |
| Introspection | __factory_man__(:variants, :user) |
Strict params
strict: true checks
| Check | When | Catches |
|---|---|---|
| Unknown keys | Before hooks and the body | %{usernme: "x"} |
| Ignored or changed params | After the body and lazy values | A body that forgets its merge |
What the second check compares
| Value given | Kept when |
|---|---|
| Plain map | Each of its keys is kept (recursively), also when built into a struct |
| Anything else | The built value is == to it |
| Function (lazy) | Not checked |
| Association key | Not checked |
Key in allow: | Not checked, by either check |
Hooks
Names and stages
| Hook | Receives | Runs |
|---|---|---|
:before_build_params | params | Before assocs: and the body |
:after_build_params | params | After the body and the strict check |
:before_build_struct | params | Before struct!/2 |
:after_build_struct | struct | After the struct is built |
:before_insert | struct | Before the default target's insert |
:after_insert | struct | After the insert |
body: :struct skips the three params-stage hooks. insert_via: targets skip the insert hooks.
Forms and placements
| Value | Meaning |
|---|---|
&M.hook/1 | One 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
| Form | Meaning |
|---|---|
&build_user_struct/1 | Builds an absent key with %{} |
fn params, factory_params -> ... end | Also 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 gives | Singular | Plural (list) |
|---|---|---|
| Key absent | default: (%{}) | default: ([]) |
nil | nil | Raises (use []) |
| Params map | Built | Raises |
| Struct | Reused | Raises |
| List | Raises | Each item resolved |
Sequences and reflection
Sequences
| Call | Values |
|---|---|
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
| Call | Returns |
|---|---|
__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 |