Vanja Ćosić

Using test fixtures to seed the database in Phoenix

While working on a Phoenix project, I wanted to reuse some of the existing fixtures from my tests to seed the database during development. But importing and calling any of the functions in seeds.exs throws an error.

The problem #

In a standard Phoenix project, iex will run in development mode (ie. with MIX_ENV=dev) and only compile files inside the /lib directory.

So trying to import fixtures that are located in the /test/support/fixtures directory will not work, even when using an import statement:

seeds.exs · elixir
import MyApp.UsersFixture

user = user_fixture()

Running the code throws this error:

shell
$ mix ecto.setup

** (UndefinedFunctionError) function MyApp.UsersFixture.user_fixture/0 is undefined (module MyApp.UsersFixture is not available)
    MyApp.UsersFixture.user_fixture()
    priv/repo/seeds.exs:21: (file)

Solution A: Require the file manually #

We can include the file at the top of our file using Code.require_file/2:

seeds.exs · elixir
Code.require_file("test/support/fixtures/users_fixtures.ex")

The function is now available in our module:

seeds.exs · elixir
user = MyApp.UsersFixtures.user_fixture()

Note:
Some older guides mention using Code.load_file/2 for the same purpose, but it was deprecated in Elixir v1.10 in favor of Code.require_file/2.

Tip:
You can pass __DIR__ as the second argument to Code.require_file/2, if you want to use a relative import path.

Solution B: Configure Mix to compile all files #

If you need to reference a lot of fixtures or functions from your /test directory, it might be easier to make them all available at once.

The mix.exs file at the root of your project defines the configuration for Mix. One of the variables is called elixirc_paths, which tells Elixir which directories to include during compilation.

mix.exs · elixir
 4
 5
 6
 7
 8
 9
10
11
12
def project do
    [
      app: :myapp,
      version: "0.1.0",
      elixir: "~> 1.14",
      elixirc_paths: elixirc_paths(Mix.env()),
      # [...]
    ]
  end

In a default Phoenix project that variable is set by the private elixirc_paths/1 function, defined further down in the same file.

We can add an extra function clause for the argument :dev and add the test/support path to the list of paths the get compiled.

mix.exs · elixir
26
27
28
29
30
31

  # Specifies which paths to compile per environment.
  defp elixirc_paths(:test), do: ["lib", "test/support"]
  defp elixirc_paths(:dev), do: ["lib", "test/support"]
  defp elixirc_paths(_), do: ["lib"]
 

Now all the fixtures and other helper functions from the test/support/ directory are available in the rest of our Phoenix project (in development mode).