A toolkit for test data generation.
The main idea of SeedFactory
is to generate data in tests according to your application business logic (read as context functions if you use Phoenix Contexts) whenever it is possible and avoid direct inserts to the database (as opposed to ex_machina
).
This approach allows you to minimize testing of invalid states as you're not forced to keep complex database structure in your head in order to prepare test data.
The library is completely agnostic to the database toolkit.
See docs for details https://hexdocs.pm/seed_factory.
The package can be installed by adding seed_factory
to your list of dependencies in mix.exs
:
def deps do
[
{:seed_factory, "~> 0.6"}
]
end
This section provides a couple of examples of what the API of the library looks like. For more comprehensive explanations please refer to docs. README is NOT the primary source of documentation.
To use the library, define a schema with commands that describe the processes of your application. When a command is executed it modifies the context by producing/updating/deleting entities.
There is a concept of traits. Think about them as labels which are assigned to produced/updated entities when specific commands with specific arguments are executed.
defmodule MyApp.SeedFactorySchema do
use SeedFactory.Schema
command :create_company do
param :name, generate: &Faker.Company.name/0
resolve(fn args ->
with {:ok, company} <- MyApp.Companies.create_company(args) do
{:ok, %{company: company}}
end
end)
produce :company
end
command :create_user do
param :name, generate: &Faker.Person.name/0
param :role, value: :normal
param :company, entity: :company
resolve(fn args -> MyApp.Users.create_user(args.company, args.name, args.role) end)
produce :user
produce :profile
end
command :activate_user do
param :user, entity: :user, with_traits: [:pending]
resolve(fn args ->
user = MyApp.Users.activate_user!(args.user)
{:ok, %{user: user}}
end)
update :user
end
trait :pending, :user do
exec :create_user
end
trait :active, :user do
from :pending
exec :activate_user
end
trait :admin, :user do
exec :create_user, args_pattern: %{role: :admin}
end
trait :normal, :user do
exec :create_user, args_pattern: %{role: :normal}
end
end
import SeedFactory
context = %{}
# Put metadata about the schema to the context with the help of init/2 function
context = init(context, MyApp.SeedFactorySchema)
# Now, we can execute a command with automatically generated args using exec/2
%{company: _} = exec(context, :create_company)
# Arguments can be passed explicitly using exec/3
%{company: _, user: _, profile: _} =
context
|> exec(:create_company, name: "GitHub")
|> exec(:create_user, name: "John Doe")
|> exec(:activate_user)
# Dependent entities are produced automatically if there is no such entity in the context.
# In this example, :create_company will be executed implicitly, because :create_user depends on :company
%{company: _, user: _} = exec(context, :create_user)
# If you're fine with generated arguments, then you can use produce/2 to specify
# desired entities and the chain of corresponding commands will be executed automatically
%{company: _company} = produce(context, :company)
%{user: _user, company: _company} = produce(context, [:company, :user])
# Rebind entities to another names
%{profile1: _, user1: _} = produce(context, user: :user1, profile: :profile1)
# Specify traits
%{user: _user} = produce(context, user: [:admin, :active])
# The command above is an alternative to
%{user: _user} =
context
|> exec(:create_user, role: :admin)
|> exec(:activate_user)
Usage with ExUnit
:
defmodule MyApp.MyTest do
use ExUnit.Case
use SeedFactory.Test, schema: MyApp.SeedFactorySchema
describe "produce/1 macro" do
produce :company, user: [:active, :admin, as: :active_admin]
test "inspect data", %{company: company, active_admin: active_admin} do
dbg(company)
dbg(active_admin)
end
end
describe "produce/2 and exec/3 functions" do
test "demo #1", ctx do
ctx =
ctx
|> exec(:create_company, name: "GitHub")
|> produce(user: [:normal, :active])
dbg(ctx)
end
test "demo #2", ctx do
ctx
|> produce(company: :company1, user: :user1, profile: :profile1)
|> produce(company: :company2, user: :user2, profile: :profile2)
|> dbg()
end
test "demo #3", ctx do
ctx = produce(ctx, :company)
ctx1 = ctx |> exec(:create_user, name: "John")
ctx2 = ctx |> exec(:create_user, name: "Jane") |> exec(:activate_user)
dbg(ctx1)
dbg(ctx2)
end
end
end