halostatue

halostatue

Ecto: Unit Test for Validating Association Declarations

I’m sharing this because it may be of general interest. I was doing some refactoring of code and removed some automatic aliases when doing use MyApp.Schema, which broke some of my unit tests…but not all of them (I don’t have 100% coverage; imagine that).

The error raised was UndefinedFunctionError for __schema__. After some thinking, this is the unit test I came up with to verify this. The only thing to maintain in it is the list of @relations, which are the application Ecto schemas that have belongs_to, has_many, or many_to_many relationships listed in them.

This will ensure that the relationships are not referring to the wrong aliased module, since Ecto does not (cannot?) verify the module specifications a compile-time. Example:

defmodule MyApp.Business do
  use MyApp.Schema

  schema "businesses" do
    has_many :users, User
  end
end

The error is fairly visible, but unless you have a test that exercises the association (via join(Business, :left, [b], assoc(b, :user)), there will be nothing that exposes the missing alias MyApp.User from the module during compilation.

Adding a test like this will help:

defmodule MyApp.ValidEctoRelationsTest do
  @moduledoc """
  Test that belongs_to, has_many, and many_to_many relationships are valid,
  since these things cannot be determined at compile time.
  """

  use ExUnit.Case

  alias MyApp.Repo

  import Ecto.Query

  setup do
    :ok = Ecto.Adapters.SQL.Sandbox.checkout(Repo)
  end

  @relations [
    MyApp.Business,
    MyApp.User
  ]

  for schema <- @relations, association <- schema.__schema__(:associations) do
    test "#{inspect(schema)} has a valid association for #{inspect(association)}" do
      assert_valid_relationship(unquote(schema), unquote(association))
    end
  end

  defp assert_valid_relationship(schema, association) do
    schema
    |> join(:left, [s], assoc(s, ^association))
    |> where(false)
    |> Repo.all()

    assert true
  rescue
    UndefinedFunctionError ->
      %{queryable: module} = schema.__schema__(:association, association)

      flunk("""
      Schema #{inspect(schema)} association #{inspect(association)} is invalid.
      The associated module #{inspect(module)} does not appear to be an Ecto
      schema. Is #{inspect(schema)} missing an alias?
      """)
  end
end

Where Next?

Popular in Discussions Top

pillaiindu
In django there is a cache framework backed by memcached. Rails also puts a lot of emphasis on caching, and even the idea of russian-doll...
New
rms.mrcs
A couple of days ago I was discussing with a friend about different approaches to write microservices. He said that if he was going to w...
New
MarioFlach
Hello, I want to share a project I’ve been working on for a while: https://github.com/almightycouch/gitgud Background Some time ago I ...
New
Fl4m3Ph03n1x
Background A few days ago I was listening to The future of Elixir from Elixir Talks, with Dave Thomas (@pragdave ) and Brian Mitchell. I...
New
Fl4m3Ph03n1x
Background This question comes mainly from my ignorance. Today is Black Friday, one of my favorite days of the year to buy books. One boo...
New
AngeloChecked
What learn first? Rust or Elixir Hi Elixir community! I’m here because i want learn a new language. I’m a junior developer and mainly i ...
New
cvkmohan
The upcoming Phoenix 1.6 release looks very interesting. Became a habit to watch the commits - and - what they are bringing in. phx.gen...
New

Other popular topics Top

Brian
What is the proper way to load a module from a file in to IEX? In the python world, doing something like this pretty standard: from ....
New
JakeBecker
TL;DR: I’ve just released an implementation of Microsoft’s IDE-independent Language Server Protocol for Elixir. It adds language support ...
1144 54996 245
New
ashish173
I am using Ecto timestamps with postgres, I can see the timestamps() use the :naive_dateime but for my use case I wanted to store the ti...
New
sen
Hi All, I set a environment variables in dev.exs , like below code. when i start server, how can i set the ${enable} value? thanks. d...
New
sergio_101
I am VERY much an elixir newbie. I have taken one elixir course and one phoenix course on Udemy. During that course, I saw the instructor...
New
AstonJ
Posting this to see if we can make things easier for people to get into Neovim. If you use Neovim and have a favourite distro please let ...
New

We're in Beta

About us Mission Statement