4

Good morning,

I have some particularly expensive database setup code in my Elixir project that inserts required data into the database.

I currently have my tests working such that this data is inserted prior to any tests that need it via a @tag :insert_my_data construct. I have code in my test helper that does something like:

setup tags do
  if tags[:insert_my_data] do
    # Run code here
  end
end

The problem with this is that I'm having to run this code hundreds of times, and it is slow.

What would work better was if the code ran once automatically, prior to any invocation of mix test, and the transaction rollback functionality provided by Ecto would reset to this known state (rather than an empty database).

How can I accomplish this?

Thanks!

Brandon
  • 3,091
  • 2
  • 34
  • 64
  • 2
    Maybe `setup_all` would help? http://elixir-lang.org/docs/v1.3/ex_unit/ExUnit.Callbacks.html#setup_all/1 – Dogbert Nov 18 '16 at 16:33
  • @Dogbert Can app code and database inserts be used within `setup_all`? Or other limitations of `setup_all`? I'm having trouble remembering right now, but I feel like I looked at this. – Brandon Nov 18 '16 at 16:37
  • @Dogbert It appears that `setup_all` runs at the start of every test case. I'd like to run the code once - and only once - at the start of the test "suite". – Brandon Nov 18 '16 at 18:33
  • What is your use case for this? – Justin Wood Nov 18 '16 at 18:44
  • @Brandon `setup_all` should run only once per test module, before the first test in that module is run, not at the start of every single test. – Dogbert Nov 18 '16 at 19:50
  • @Dogbert yes - it does run only once at the start of a given test module. I'm wondering if there's a way to run code once (and only once), before *all* modules - i.e. If I have 20 test modules, I only want the code to be run once. A "global" `setup_all` so to speak. – Brandon Nov 18 '16 at 22:03
  • @JustinWood I have a multi-tenant database setup. It is reasonably expensive to insert a new tenant with the default data that gets inserted on registration. I'd like to insert a tenant before any tests are run, and have that tenant be shared with *all* test modules/cases, not just one (which is what you get when you use `setup_all`). – Brandon Nov 18 '16 at 22:04
  • I haven't done it before, but have you thought about putting the code in your test_helper.exs file? – Justin Wood Nov 18 '16 at 22:16
  • @JustinWood I hadn't tried that, I'll give it a shot. – Brandon Nov 18 '16 at 23:03
  • 1
    You can create an alias for your `mix` tasks - and this db-refresh can be done there. It will work for a single test or any group of tests as a whole. For eg, my `mix.exs` file has this: ``` defp aliases do [ "ecto.seed": "run priv/repo/seeds.exs", "ecto.setup": ["ecto.create", "ecto.migrate", "ecto.seed"], "ecto.reset": ["ecto.drop", "ecto.setup"], test: ["ecto.drop", "ecto.create --quiet", "ecto.migrate", "test"], commit: ["format", "deps.get --only #{Mix.env()}", "coveralls.html", "credo --strict"] ] end ``` – Vijay Raghavan Aravamudhan Nov 18 '18 at 10:47

1 Answers1

0

I know this is an old question, but it's a good one and it still pops up in web search results.

Another way to have a callback be run before any tests that get executed is to put it in the test/test_helper.exs file. By default it contains only

ExUnit.start()

and it will be executed once, before the test run, whether running just mix test or targeting a subset of the tests with something like mix test test/path/to/file_test.exs:42.

I feel like it's cleaner than modifying the aliases, if you're targeting just the test runs.

nietaki
  • 8,758
  • 2
  • 45
  • 56