ExZample
A scalable error-friendly factories library for your Elixir apps
Installation
If available in Hex, the package can be installed
by adding ex_zample
to your list of dependencies in mix.exs
:
def deps do
[
{:ex_zample, "~> 0.2.0"}
]
end
Documentation can be generated with ExDoc and published on HexDocs. Once published, the docs can be found at https://hexdocs.pm/ex_zample.
Quick Start
You can build any struct by passing a struct module:
iex> ExZample.build(User)
%MyApp.User{first_name: nil, age: 21}
ExZample
will automatically use the default values inside of that struct. If
you want to define different values, you need to implement the example/0
callback.
defmodule MyApp.User do
@behaviour ExZample
defstruct first_name: nil, age: 21
@impl true
def example do
%__MODULE__{first_name: "Abili De Bob", age: 12}
end
end
iex> ExZample.build(MyApp.User)
%MyApp.User{first_name: "Abili De Bob", age: 12}
If you don't want to mix your test data with your app code, you can define the factory in a different module:
defmodule MyApp.User do
defstruct first_name: nil, age: 21
end
defmodule MyApp.Factories.UserFactory do
@behaviour ExZample
alias MyApp.User
@impl true
def example do
%User{first_name: "Abili De Bob", age: 12}
end
end
iex> alias MyApp.Factories.UserFactory
iex> ExZample.build(UserFactory)
%MyApp.User{first_name: "Abili De Bob", age: 12}
Building your factories
You can use the build/2
, build_pair/2
and build_list/3
to generate data
without side-effects.
iex> ExZample.build(UserFactory, age: 42)
%MyApp.User{first_name: "Abili De Bob", age: 42}
iex> ExZample.build_pair(UserFactory, age: 42)
{%MyApp.User{first_name: "Abili De Bob", age: 42}, %MyApp.User{first_name: "Abili De Bob", age: 42}}
iex> ExZample.build_list(100, UserFactory, age: 42)
[
%MyApp.User{first_name: "Abili De Bob", age: 42},
%MyApp.User{first_name: "Abili De Bob", age: 42},
# ...
]
When you pass attributes, it will override the default ones defined in your factories.
Inspiration
This library was strongly inspired by:
Why not other factories libraries?
Right now you shouldn't change for this one. The other factories libraries has much more features.
However, when your codebase starts to get bigger, it's nice to have a way to split up your factories files in multiple files. Also, it's important when a error happens, it should be explicit and easy to reason about.
Most of other Elixir libraries relies on DLS with macro code, and you need to write macros to split up your factories files. When you get some error in your factory, it's very hard to locate where is the real problem.
This library approach is to rely on vanilla Elixir modules and contracts based on Elixir behaviours. No need to use any macro. The purpose for any macro that can shows up here will be for syntax sugar, not part of the main functionality.
Do I really need a factory library?
Probably not, you can define your own modules to return example structs and insert them with your repo module. However, a factory library can give you some convenient functions for you don't need to reinvent the wheel.