slouchpie
I wanted to run some LiveTests with
use MyApp.ConnCase, async: true
but I was having a problem because, in async mode, each process gets its own Sandbox connection.
In a default Phoenix app, this is not a problem because of this line in test_helper.exs:
Ecto.Adapters.SQL.Sandbox.mode(MyApp.Repo, :manual)
However, I removed that line from my project because in manual mode tests break when using worker processes in application.ex that access the database.
This means that data available to the 1st mount is different to the data available in the 2nd mount.
I humbly present one “hacky” way of making LiveView tests async.
We want to “allow” the 2nd mount process to use the 1st mount processes DB connection, so:
Make an on_mount module
defmodule MyAppWeb.OnMounts.AllowSandboxForLiveView do
@moduledoc false
alias Ecto.Adapters.SQL.Sandbox
alias MyApp.Repo
alias Phoenix.LiveView
def on_mount(:default, _params, session, socket) do
socket
|> tap(&allow_sandbox(&1, session))
|> then(&{:cont, &1})
end
defp allow_sandbox(socket, session) do
with true <- LiveView.connected?(socket),
unconnected_pid when is_pid(unconnected_pid) <- Map.get(session, "unconnected_pid"),
true <- self() != unconnected_pid do
Sandbox.allow(Repo, unconnected_pid, self())
end
end
end
Make a plug to get the PID in the session
defmodule MyAppWeb.Plugs.PutUnconnectedPidInSession do
@moduledoc """
Put the PID in the session, for testing purposes ONLY.
"""
import Plug.Conn
def init(_opts), do: nil
if Application.compile_env(:my_app, :env) == :test do
def call(conn, _opts), do: put_session(conn, :unconnected_pid, self())
else
def call(conn, _opts), do: conn
end
end
Use the plug in router.ex
pipeline :browser do
# ...
if Application.compile_env(:my_app, :env) == :test do
plug MyAppWeb.Plugs.PutUnconnectedPidInSession
end
#...
Use the on_mount in all LiveViews. Go to my_app_web.ex
def live_view do
#...
if Application.compile_env(:my_app, :env) == :test do
on_mount MyAppWeb.OnMounts.AllowSandboxForLiveView
end
#...
And, finally, use the on_mount in all live_session in the router.ex.
if Application.compile_env(:my_app, :env) == :test do
@preliminary_on_mounts [MyAppWeb.OnMounts.AllowSandboxForLiveView]
else
@preliminary_on_mounts []
end
live_session,
#...
on_mount: @preliminary_on_mounts ++ [
AssignUser,
RequireUser,
#...etc
This is hacky and a bit ugly in the live_session bit but it makes LiveView work with
use MyApp.ConnCase, async: true
What is happening here:
As far as I know, the only way to share data between disconnected and connected mount is to use the session. In test environment only, put the PID in the session. Then during the connected mount use that PID to allow access to the Sandbox connection.
Note: if you want the Application.compile_env(:my_app, :env) to work then just add this line to config/test.exs:
config :my_app, :env, :test
And similar for dev/prod.
I eagerly welcome any and all feedback.
Trending in Questions
Other Trending Topics
Categories:
Sub Categories:
Forums
Popular Tags
- #ecto
- #liveview
- #troubleshooting
- #learning-elixir
- #deployment
- #library
- #erlang
- #testing
- #genserver
- #mix
- #absinthe
- #remote-other
- #otp
- #plug
- #how-to-question
- #macros
- #postgres
- #elixirconf
- #channels
- #exunit
- #discussion
- #code-sync
- #javascript
- #podcasts
- #onsite
- #dialyzer
- #docker
- #authentication
- #umbrella
- #full-time-contract
- #podcasts-by-brainlid
- #ecto-query
- #blog-post
- #elixir-ls
- #elixirconf-us
- #ai
- #phoenix_html
- #iex
- #graphql
- #genstage
- #websockets
- #supervisor
- #advent-of-code
- #distillery
- #processes
- #api
- #forms
- #hex
- #security
- #metaprogramming










Showing Posts 1 to 10- Show Best Posts
- Show All (oldest first)
- Show All (newest first)
LostKobrakai
This looks a lot like Phoenix.Ecto.SQL.Sandbox — Phoenix/Ecto v4.6.3
slouchpie
I have never seen this. Thanks!
slouchpie
Does this allow LiveView (and other ConnCase) tests to run async?
LostKobrakai
Normal ConnTests and LiveViewTest should work without messing with the sandbox. Dealing with the sandbox manually is only needed if you want to test only via http/websockets for integration tests through browsers.
hubertlepicki
which I suspect is what @slouchpie wants to do. Wallaby/browser tests?
slouchpie
I do not want to do acceptance tests. I just wanted to run my LiveView tests async.
@LostKobrakai does the approach in the link you posted above allow ConnCase tests to run async?
hubertlepicki
I do not think this should be required to do any custom plumbing in this set up.
Are you following this example? Phoenix.LiveViewTest — Phoenix LiveView v1.2.5
slouchpie
The “normal” setup as generated by
mix phx.newhas the sandbox running in manual mode, which is why the conn case tests can run async. However, this causes more complicated applications to crash in test, since they usually have worker processes that interact with the database.slouchpie
The custom plumbing was already done
I removed this line from
test_helper.exshubertlepicki
I see. So you want to keep these background workers running in test and then also run the liveview tests in async mode?