Using setup_all with database?

My use case is when using with Triplex, in every test i need a “tenant” to exist. The default setup where a tenant is created in every test is unacceptably slow (each test takes >1 second).

My solution is to make a new module called TenantCase:

defmodule MyApp.TenantCase do
  use ExUnit.CaseTemplate

  setup_all tags do
    :ok = Ecto.Adapters.SQL.Sandbox.checkout(MyApp.Repo)
    # we are setting :auto here so that the data persists for all tests,
    # normally (with :shared mode) every process runs in a transaction
    # and rolls back when it exits. setup_all runs in a distinct process
    # from each test so the data doesn't exist for each test.
    Ecto.Adapters.SQL.Sandbox.mode(MyApp.Repo, :auto)
    {:ok, tenant} = MyApp.Tenant.create(%{name: "example"})

    on_exit fn ->
      # this callback needs to checkout its own connection since it
      # runs in its own process
      :ok = Ecto.Adapters.SQL.Sandbox.checkout(MyApp.Repo)
      Ecto.Adapters.SQL.Sandbox.mode(MyApp.Repo, :auto)

      # we also need to re-fetch the %Tenant struct since Ecto otherwise
      # complains it's "stale"
      tenant = MyApp.Tenant.get!(tenant.id)
      MyApp.Tenant.delete(tenant)
      :ok
    end

    [tenant: tenant]
  end
end

Now i can use the tenant in my tests:

defmodule MyApp.MyTest do
  use MyApp.ConnCase
  use MyApp.TenantCase

  test "foo", %{tenant: tenant} do
    ...
  end
end
20 Likes