I’m sharing this because it may be of general interest. I was doing some refactoring of code and removed some automatic alias
es 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