Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Best practice for testing Elixir Ecto model associations

I'm trying to test a belongs to association in Elixir.

Say I have two models, a Product and a ProductType. Products belong to a product type.

defmodule Store.Product do
  use Store.Web, :model

  schema "products" do
    field :name, :string

    belongs_to :type, Store.ProductType, foreign_key: :product_type_id

    timestamps
  end

  @required_fields ~w(name product_type_id)

  @optional_fields ~w()

  def changeset(model, params \\ :empty) do
    model
    |> cast(params, @required_fields, @optional_fields)
  end
end

defmodule Store.ProductType do
  use Store.Web, :model

  schema "product_types" do
    field :name, :string

    timestamps
  end

  @required_fields ~w(name)
  @optional_fields ~w()

  def changeset(model, params \\ :empty) do
    model
    |> cast(params, @required_fields, @optional_fields)
  end
end

Here's what I have in my test file:

defmodule Store.ProductTest do
  use Store.ModelCase

  alias Store.Repo
  alias Store.Product
  alias Store.ProductType

  @valid_attrs %{
    name: "pickles", 
    product_type_id: 42,
  }

  @invalid_attrs %{}

  test "product type relationship" do
    product_type_changeset = ProductType.changeset(
      %ProductType{}, %{name: "Foo"}
    )
    product_type = Repo.insert!(product_type_changeset)

    product_changeset = Product.changeset(
      %Product{}, %{@valid_attrs | product_type_id: product_type.id}
    )
    product = Repo.insert!(product_changeset)

    product = Product |> Repo.get(product.id) |> Repo.preload(:type)
    assert product_type == product.type
  end
end

I'm basically creating a product type, creating a product, fetching the product record from the database and verifying that the type is the same as one I created.

Is this a reasonable approach?

EDIT

For posterity's sake, here is a cleaner test without using changesets:

test "belongs to product type" do
  product_type = Repo.insert!(%ProductType{})
  product = Repo.insert!(%Product{product_type_id: product_type.id})
  product = Product |> Repo.get(product.id) |> Repo.preload(:type)
  assert product_type == product.type
end

To test this association, you can essentially forgo casting and validations.

like image 346
Elliot Larson Avatar asked Nov 26 '15 00:11

Elliot Larson


People also ask

What is Ecto Elixir?

Ecto is an official Elixir project providing a database wrapper and integrated query language. With Ecto we're able to create migrations, define schemas, insert and update records, and query them. Changesets. In order to insert, update or delete data from the database, Ecto. Repo.

How do you run an Elixir test?

You can now do mix test path/to/test. exs:13 , where 13 is the line of the test. For umbrella project, do not include apps/app_name in the file path. Even if the file path from root directory is apps/app_name/test/app_name_test.

Is Ecto an ORM?

Unlike ActiveRecord, Ecto is not an ORM, but a library that enables the use of Elixir to write queries and interact with the database. Ecto is a domain specific language for writing queries and interacting with databases in Elixir.


2 Answers

I would not test this explicitly at all - you're basically testing Ecto here.

This sort of thing I usually test implicitly in e.g. a controller test, where you post something and then make sure that the correct data was created in the DB.

If you want to have a unit test for that, you need to think about what exactly you want to compare. It should be enough to test that the inserted product type's id is the same as the inserted product's product_type_id, but that feels weird because then it's more obvious that you're just testing ecto functionality here.

like image 194
manukall Avatar answered Sep 28 '22 11:09

manukall


A great reference for testing an Ecto association is the actual tests Ecto uses for testing associations. You can check it out on their GitHub.

https://github.com/elixir-ecto/ecto/blob/master/test/ecto/schema_test.exs

Here's an example:

test "belongs_to account" do
  association =
    %Ecto.Association.BelongsTo{cardinality: :one, defaults: [],
                                field: :account, on_cast: nil,
                                on_replace: :raise, owner: AccountUser,
                                owner_key: :account_id, queryable: Account,
                                related: Account, related_key: :id,
                                relationship: :parent, unique: true}

  assert AccountUser.__schema__(:association, :account) == association
end
like image 34
anthonator Avatar answered Sep 28 '22 12:09

anthonator