# ExMachina

[![Circle CI](](

ExMachina makes it easy to create test data and associations. It works great
with Ecto, but is configurable to work with any persistence library.

## Installation

In `mix.exs`, add the ExMachina dependency:

def deps do
  [{:ex_machina, "~> 0.4"}]

Add `:ex_machina` to your application list:

def application do
  [applications: app_list(Mix.env)]

defp app_list(:test), do: [:ex_machina | app_list]
defp app_list(_),  do: app_list
defp app_list,  do: [:logger]

## Cheatsheet

Check out [the docs]( for more details.

Define factories:

defmodule MyApp.Factory do
  # with Ecto
  use ExMachina.Ecto, repo: MyApp.Repo

  # without Ecto
  use ExMachina

  def factory(:user, _attrs) do
      name: "Jane Smith",
      email: sequence(:email, &"email-#{&1}"),

  def factory(:article, attrs) do
      title: "Use ExMachina!",
      # has_many/has_one associations are inserted when you call `create`
      comments: [build(:comment)],
      # only available in ExMachina.Ecto
      author: assoc(attrs, :author, factory: :user),

  def factory(:comment, attrs) do
      text: "It's great!",
      article: assoc(attrs, :article),

Use factories:

# `build*` returns an unsaved comment.
# Associated records defined on the factory are built.
attrs = %{body: "A comment!"} # attrs is optional. Also accepts a keyword list.
build(:comment, attrs)
build_pair(:comment, attrs)
build_list(3, :comment, attrs)

# `create*` returns a saved comment.
# Associated records defined on the factory are built and saved.
create(:comment, attrs)
create_pair(:comment, attrs)
create_list(3, :comment, attrs)

# `fields_for` returns a plain map without any Ecto specific attributes.
# This is only available when using `ExMachina.Ecto`.
fields_for(:comment, attrs)

# Flexible Factories with Pipes

def make_admin(user) do
  %{user | admin: true}

def with_article(user) do
  create(:article, user: user)

build(:user) |> make_admin |> create |> with_article

## Using with Phoenix and Ecto

There is nothing special you need to do with Phoenix unless you decide to
`import` your factory module.

By default Phoenix imports `Ecto.Model` in the generated `ConnCase` and
`ModelCase`  modules (found in `test/support/conn_case.ex` and
`test/support/model_case.ex`). To import your factory we recommend excluding
`build/2` or aliasing your factory instead.

# in test/support/conn_case|model_case.ex

# Add `except: [build: 2] to the `Ecto.Model` import
import Ecto.Model, except: [build: 2]

## Usage in a test

defmodule MyApp.MyModuleTest do
  use MyApp.ConnCase
  # You can also import this in your MyApp.ConnCase if using Phoenix
  import MyApp.Factory

  test "shows comments for an article" do
    conn = conn()
    article = create(:article)
    comment = create(:comment, article: article)

    conn = get conn, article_path(conn, :show,

    assert html_response(conn, 200) =~ article.title
    assert html_response(conn, 200) =~ comment.body

## Using without Ecto

You can use ExMachina without Ecto, by using just the `build` function, or by
defining `save_record/1` in your module.

defmodule MyApp.JsonFactory do
  use ExMachina

  def factory(:user, _attrs) do
    %User{name: "John"}

  def save_record(record) do
    # Poison is a library for working with JSON

# Will build and then return a JSON encoded version of the map

You can do something similar while also using Ecto by defining a new function.
This gives you the power to call `create` and save to Ecto, or call `build_json`
or `create_json` to return encoded JSON objects.

defmodule MyApp.Factory do
  use ExMachina.Ecto, repo: MyApp.Repo

  def factory(:user, _attrs) do
    %User{name: "John"}

  # builds the object and then encodes it as JSON
  def build_json(factory_name, attrs) do
    build(factory_name, attrs) |> Poison.encode!

  # builds the object, saves it to Ecto and then encodes it
  def create_json(factory_name, attrs) do
    create(factory_name, attrs) |> Poison.encode!

## Contributing

Before opening a pull request, please open an issue first.

    $ git clone
    $ cd ex_machina
    $ mix deps.get
    $ mix test

Once you've made your additions and `mix test` passes, go ahead and open a PR!

## License

ExMachina is Copyright © 2015 thoughtbot. It is free software, and may be
redistributed under the terms specified in the [LICENSE](/LICENSE) file.

## About thoughtbot


ExMachina is maintained and funded by thoughtbot, inc.
The names and logos for thoughtbot are trademarks of thoughtbot, inc.

We love open source software!
See [our other projects][community] or
[hire us][hire] to design, develop, and grow your product.


## Inspiration

* [Fixtures for Ecto](
* [Factory Girl](