Testing domains/contexts: relations in Ecto schemas

Hi,

I am currently writing some tests for my domains/contexts. I use ExMachina for generating the test data. I have a domain for comments on articles, to build the test data I have to first create an article and then the comment for it.

On the other hand my API doesn’t preload the article since I don’t need it. This leads to the problem that the generated tests no longer match:

comment = insert(:comment)
assert Articles.list_comments() == [comment]

generates the error Assertion with == failed because list_comments() returns the comment with #Ecto.Association.NotLoaded<association :article is not loaded>} while the comment variable contains the article.

I could match the relevant struct members by writing them out, but I wonder if there is a nice solution?

Best regards,

CK

Just remembered this question. I came up with the following function:

def unload_relations(obj, to_remove \\ nil) do
  assocs =
    if to_remove == nil,
      do: obj.__struct__.__schema__(:associations),
      else: Enum.filter(obj.__struct__.__schema__(:associations), &(&1 in to_remove))

  Enum.reduce(assocs, obj, fn assoc, obj ->
    assoc_meta = obj.__struct__.__schema__(:association, assoc)

    Map.put(obj, assoc, %Ecto.Association.NotLoaded{
      __field__: assoc,
      __owner__: assoc_meta.owner,
      __cardinality__: assoc_meta.cardinality
    })
  end)
end

This artificially removes the association and sets them to the appropriate Ecto.Association.NotLoaded value.

2 Likes