Ole Spaarmann
Ole Spaarmann

Reputation: 16761

Elixir/Phoenix: How to do unit tests for models without using Ecto?

I'm building a elixir phoenix app using a custom database adapter to connect to OrientDB. So I generated the base app with the --no-ecto option since I'm not using Ecto.

I'm building custom models and custom validations but of course want to do unit testing.

If I try to include ModelCase in my unit tests like so:

defmodule App.UserTest do
  use App.ModelCase

end

I get the error

module App.ModelCase is not loaded and could not be found

Which is probably because it is part of Ecto.

If I don't include it, the code fails later, telling me that

undefined function test/2

How can I deal with that?

Upvotes: 5

Views: 3559

Answers (2)

tkowal
tkowal

Reputation: 9299

Short answer: instead of use App.ModelCase just use use ExUnit.Case.

Long answer. When creating project with Ecto you get three different test case templates located in test/support:

  • channel_case.ex
  • conn_case.ex
  • model_case.ex

Case templates are used to define functions that can be used in every test that uses the template.

For example the model_case defines this for you:

using do
  quote do
    alias App.Repo

    import Ecto
    import Ecto.Changeset
    import Ecto.Query, only: [from: 1, from: 2]
    import App.ModelCase
  end
end

setup tags do
  unless tags[:async] do
    Ecto.Adapters.SQL.restart_test_transaction(App.Repo, [])
  end

  :ok
end

def errors_on(model, data) do
  model.__struct__.changeset(model, data).errors
end

Everything inside quote do ... end is injected at the beginning of your test case. This not useful at all if you are not using Ecto. First line aliases repo, next lines import Ecto modules (which you don't have)

Setup function makes sure that tests are run inside transaction that can be rolled back when they finish and errors_on is also Ecto specific. That is why this module is not present at all when you run with --no-ecto.

So you have two options. You can either just use ExUnit.Case which is standard way of dealing with tests (you can check that by creating non-Phoenix application with mix) or you can create your own App.ModelCase. This might be a good idea if there is enough shared code between your model test cases.

Upvotes: 5

Cody Poll
Cody Poll

Reputation: 8280

App.ModelCase is not automatically generated with the --no-ecto option.

It itself does use ExUnit.CaseTemplate (See the CaseTemplate docs), and injects an import App.ModelCase into the using module. It also imports some ecto-specific modules, but they're not useful to you.

In your case, you can define App.ModelCase in test/support/model_case, use ExUnit.CaseTemplate inside it, and define what you want injected into the using module by defining a using macro.

Just to give you an idea of some of the things you might want to do, here's an example ecto-specific version:

defmodule App.ModelCase do
  use ExUnit.CaseTemplate

  using do
    quote do
      alias App.Repo

      import Ecto
      import Ecto.Changeset
      import Ecto.Query, only: [from: 1, from: 2]
      import App.ModelCase
    end
  end

  setup tags do
    # Setup stuff you want to automatically do before tests 
  end

  def some_helper_function
    # helper function stuff goes here
    #
    # this function is available in all using modules because of 
    # the `import App.ModelCase` in the using macro.
  end
end

So either:

  1. Create the file
  2. Remove the stuff that's not relevant to your app
  3. Add your own stuff

or just use ExUnit.Case instead of use App.ModelCase. Either way should work.

use ExUnit.Case is simpler. use App.ModelCase will allow you to define helpers that are available in all using modules.

Upvotes: 1

Related Questions