What is the idiomatic way to cleanup test database with Ecto?

Hi team,

I’m using ex_machina to create test fixture data in my test spec.
It works fine to create data in my database and serve my test.

However, I would like to have a clean test database after each test.
In Ruby, I would use database_cleaner to do so. I wonder if there is any package in Elixir which is similar to that. (I have tried Google it and no luck).

My question is what is the idiomatic way to clean up database?

Thank you very much in advance.

Cheers,
Bang

2 Likes

Don’t the Ecto test functions operate in a sandbox? So it uses existing pre-filled data (or none if you want), and everything in each test is run in its own little sandbox? Thus I have no clue what a database_cleaner would be needed for? Or does it do something other than what the name implies?

3 Likes

Unless you’re putting stuff in the database outside of a test transaction there is no need to manually clean anything up.

2 Likes

Hi OvermindDL1 and Ben,

Thank you for your reply.

Do I have to setup my test suit so Ecto will run in sandbox mode? Or it’s default in test mode.

After my test was ran, the test data always persisted in my test database.
So I suspect I did not setup my test suit correctly. However I cannot figure it out.

Here is my test

  test "a person exists returns facebook person" do
    # TODO: Cleanup data after each test.
    insert(:test_data)

    conn = conn(:get, "test_url")
      |> use_basic_auth(@username, @password)
      |> Myapp.call(@opts)

    # Assert the response and status
    assert conn.state == :sent
    assert conn.status == 200

    {:ok, data} = Poison.decode(conn.resp_body)
    assert data["id"] == "id"
    assert data["type"] == "person"
  end

config/test.exs

use Mix.Config

config :user_store_ex, :basic_auth, [
  realm: "BasicAuth",
  username: "admin",
  password: "secret"
]

config :myapp, MyApp.Repo,
  adapter: Ecto.Adapters.Postgres,
  database: "database-test",
  hostname: "localhost"

factory.ex

defmodule Factory do
  use ExMachina.Ecto, repo: UserStoreEx.Repo

  def user_store_data_factory do
    %MyApp.MyData {
      id: "id",
      type: "person",
      data: "{}"
    }
  end
end

I used ex_machina to generate test fixture. Is there anything to deal with that?

Thank you very much,
Bang

2 Likes

Your repo config should have

pool: Ecto.Adapters.SQL.Sandbox,

in test.exs. For you that’ll look like

config :myapp, MyApp.Repo,
  adapter: Ecto.Adapters.Postgres,
  database: "database-test",
  hostname: "localhost",
  pool: Ecto.Adapters.SQL.Sandbox
6 Likes

Hi Ben,

After adding pool: Ecto.Adapters.SQL.Sandbox to my test.exs

config :myapp, MyApp.Repo,
  adapter: Ecto.Adapters.Postgres,
  pool: Ecto.Adapters.SQL.Sandbox,
  database: "database-test",
  hostname: "localhost"

The data still persists after running the test though.

Thanks,
Bang

1 Like

You’re going to need to do an initial reset of all the data that’s already in there. You can read more about its use here: https://hexdocs.pm/ecto/Ecto.Adapters.SQL.Sandbox.html

Did you set this up with phoenix? usually it populates these values for you already.

3 Likes

Thanks Ben.

Did you set this up with phoenix?

No, I only use Plug and Ecto for my REST API.

Thanks,
Bang

1 Like

I figured out how to set up Ecto to run in Sandbox mode.

Need to add Ecto.Adapters.SQL.Sandbox.mode(UserStoreEx.Repo, :manual) in test_helper.exs and
put

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

In beginning of the test file.

Thanks to everyone contributed to this thread.

Cheers,
Bang

4 Likes

Is there anything else that needs to be done? I’m getting this when I’m trying to use sandbox adapter:

** (CaseClauseError) no case clause matching: {:error, :invalid_message}
(db_connection) lib/db_connection/ownership.ex:56: DBConnection.Ownership.ownership_checkout/2

I document it a gist. Hope this help.

2 Likes

I stumbled around trying to get this to work and finally did. Seems like maybe I needed to run MIX_ENV=test mix clean after getting all these settings right? Does that even make sense?

1 Like

Theoretically you never need to run mix clean but, if you did and it fixed things, it is because something left some dirty state behind (which it was not supposed to). A way to reproduce the dirty state would be appreciated but I realize it may take a lot of time to do so.

1 Like

I did this…

MIX_ENV=test mix.ecto.reset
2 Likes

This fixed it for me, that was really crazy. I wonder why it fixed the issue.