We can't find the internet
Attempting to reconnect
Something went wrong!
Hang in there while we get back on track
Sandbox modes in Ecto Testing
Deankinyua
A sandbox is an isolated testing environment that enables users to run programs or open files without affecting the application, system or platform on which they run. Ecto provides a sandbox that wraps each test in a transaction, making sure the tests are isolated and can run concurrently.
There are a couple of modes in which the Ecto sandbox runs. When started, the sandbox pool is in automatic mode which means the Repo automatically grants connections to whoever needs them. There are 2 other modes which are important to know as well. The first one is manual which is suitable for running async tests in. You usually set this mode in your test_helper.exs at the end of the file:
ExUnit.start()
Ecto.Adapters.SQL.Sandbox.mode(SkepticBot.Repo, :manual)
In manual mode you have to explicitly allow another process to use the same connection if need be. Consider a case where you have a GenServer that does a resource update and you call it from inside the test:
test "prediction handler uses the last message from Replicate to update the question",
%{
prediction_id: prediction_id,
question: question,
output: output
} do
send(PredictionHandler, {:register_prediction, prediction_id, {self(), question}})
send(PredictionHandler, {:prediction_completed, prediction_id, output})
question = Prompts.get_question(question.id)
assert question.title == "The title"
assert question.description == "The description"
assert_receive {:prediction_result, {"The title", "The description"}}
end
Here we are sending a message (:prediction_completed) to our GenServer (PredictionHandler) which does a database update using the output value. We will see the following in the logs :
** (DBConnection.OwnershipError) cannot find ownership process for #PID<0.400.0>.
When using ownership, you must manage connections in one
of the four ways:
...
To solve this we need to allow our GenServer access to our connection using allow/4:
allow = Process.whereis(PredictionHandler)
Sandbox.allow(Repo, self(), allow)
send(PredictionHandler, {:register_prediction, prediction_id, {self(), question}})
send(PredictionHandler, {:prediction_completed, prediction_id, output})
question = Prompts.get_question(question.id)
assert question.title == "The title"
assert question.description == "The description"
assert_receive {:prediction_result, {"The title", "The description"}}
The last mode is the shared one. In this mode you will no longer have to allow individual processes access to the parent’s connection.
setup do
# Explicitly get a connection before each test
:ok = Sandbox.checkout(Repo)
# Setting the shared mode must be done only after checkout
Sandbox.mode(Repo, {:shared, self()})
end
The disadvantage of this mode is that you can no longer run your tests concurrently.
Copy link
copied to clipboard