Testing with Phoenix using MySQL setup with a Master and Slave Database

I want to share a solution to a scenario that I am trying to solve with my Phoenix project. Before I dive into that though, I first want to thank benwilson512, trevoke, law, and elbow_jason for giving me a hand on the #phoenix channel in Slack.

For my project I need to use MySQL as the database. I have configured both a Master and a Slave database server and configured my Phoenix project to use it by having a Repo and RepoSlave. The replication is working great and before I did any automated tests, I confirmed that when records are created in the Master, I can retrieve them in the Slave.

In my Phoenix code, any function that reads data uses the RepoSlave. For any Insert, Update or Delete, I use Repo. When I tried to execute my test suite, which is just the controller test files that are created when I used the Phoenix generators, I got a bunch of failing tests.

I soon discovered that although I got data in the Phoenix app when my tests ran, the records never were written to my Master database. And if a record doesn’t write to the Master, it doesn’t replicate to the Slave. Between some reading and the help in Slack, I was able to see and confirm that by using the Sandbox which my app defaulted to in the configuration, I was not getting any records written to the database.

Now there might be a better way to solve my scenario, but what I did was to comment out the Sandbox reference in my test/test_helper.exs file. I also removed any references to Sandbox in my config/test.exs file. I also found, through trial and error, that I needed to remove/comment out Sandbox references in test/support/conn_case.ex and test/support/data_case.ex.

The above got me further but then I ran into a new problem. I needed a way to ‘clean up’ after each test. Through some splunking and trying out a few things, I settled on using:

# test/test_helper.exs
ExUnit.start()

defmodule DatabaseCleaner do
  def cleanup do
    Code.compiler_options(ignore_module_conflict: true)

    Mix.Tasks.Ecto.Migrate.run(["--all", "Maestro.Repo"])

    ExUnit.Callbacks.on_exit fn ->
      Mix.Tasks.Ecto.Rollback.run(["--all", "Maestro.Repo"])
    end
  end
end


# test/maestro_web/controllers/datacenter_controller_test.exs
setup %{conn: conn} do
  DatabaseCleaner.cleanup
  {:ok, conn: put_req_header(conn, "accept", "application/json")}
end

With that addition to my test_helper I can now use the DatabaseCleaner.cleanup in my other basic controller tests.

I would love to know if there is a better way. The app I’m working on is data intensive and we do a lot of querying/assembling of data. I want to be able to use automated tests but those tests need to be able to support working with writing to the Master DB and reading from the Slave.

Thank you,

Hogi…

2 Likes