Phoenix, testing controller for model with assosiation

I’m following Programming Phoenix and code along but I try to add something on my own to learn by experimenting, and I need some advice. I don’t know how to make proper “fixture” in controller test.

My Video model looks like this:

defmodule Rumbl.Video do
  use Rumbl.Web, :model

  schema "videos" do
    field :url, :string
    field :title, :string
    field :description, :string
    belongs_to :user, Rumbl.User
    belongs_to :category, Rumbl.Category

    timestamps()
  end

  @doc """
  Builds a changeset based on the `struct` and `params`.
  """
  def changeset(struct, params \\ %{}) do
    struct
    |> cast(params, ~w(url title description category_id))
    |> validate_required([:url, :title, :description, :category_id])
    |> assoc_constraint(:category)
  end

I added assoc_constraint(:category) to make sure that every video is saved with category_id. Category model is simple, it has only name.
My test looks like this:

defmodule Rumb.VideoControllerTest do
  use Rumbl.ConnCase

  alias Rumbl.Video

  @valid_attrs %{url: "http://youtu.be", title: "vid", description: "a vid"}
  @invalid_attrs %{title: "invalid"}
..........
  @tag login_as: "max"
  test "creates user videos and redirects", %{conn: conn, user: user} do
    category = insert_category(%{name: "Drama"})
    attrs = Map.merge(@valid_attrs, %{category_id: category.id})

    conn = post(conn, video_path(conn, :create), video: attrs)
    assert redirected_to(conn) == video_path(conn, :index)
    assert Repo.get_by!(Video, @valid_attrs).user_id == user.id
  end

I don’t want to repeat that part in every test that may need that, because it does not look DRY:

    @valid_attrs %{url: "http://youtu.be", title: "vid", description: "a vid"}
    ...
    category = insert_category(%{name: "Drama"})
    attrs = Map.merge(@valid_attrs, %{category_id: category.id})

So the question is, what can I do, to ensure that some, chosen by me test will have Category? In Rails using FactoryGirl I’ll would just create a lazy evaluated Category, and also lazy evaluated @valid_attrs with that particular category_id. And the test would only hit the database (ie. create category and return it’s id) only when i would use that @valid_attrs. But how can I do it in Phoenix, so that I won’t have to repeat creating category and merging params map with proper category_id?

Is making a private function like this:

defp valid_params_map do
  category = insert_category(%{name: "Drama"})
  Map.merge(@valid_attrs, %{category_id: category.id})
end

Is a proper way? Or maybe I should do something else? What is a proper strategy for that? How do you do it? :slight_smile:

1 Like

I suggest you use ex_machina instead. Perhaps you have already found a solution, but I want to answer the question for the newbie.