slouchpie

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.

Showing Posts 1 to 10

LostKobrakai

LostKobrakai

slouchpie

slouchpie OP

I have never seen this. Thanks!

slouchpie

slouchpie OP

Does this allow LiveView (and other ConnCase) tests to run async?

LostKobrakai

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

hubertlepicki

which I suspect is what @slouchpie wants to do. Wallaby/browser tests?

slouchpie

slouchpie OP

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

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

slouchpie OP

The “normal” setup as generated by mix phx.new has 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

slouchpie OP

The custom plumbing was already done :slight_smile:
I removed this line from test_helper.exs

Ecto.Adapters.SQL.Sandbox.mode(MyApp.Repo, :manual)
hubertlepicki

hubertlepicki

I see. So you want to keep these background workers running in test and then also run the liveview tests in async mode?

Where Next? Top

Trending in Questions Top

Blokh
Hey guys, I’ve got a huge CSV ( around 10 GB ) that needs to be processed hourly Do you guys have any suggestions what is the best prac...
New
kszambelanczyk
Hello! Could someone please give me a help/sample code, how to delete a file from s3 using waffle/waffle_ecto from Phoenix app. I creat...
New
RemyXRenard
I’m seeing that a list inside a Kino.DataTable will be interpreted as a charlist, even if the Kino.configure() is set to charlists: :as_l...
New
matt-savvy
Anyone here using Honeybadger? My Honeybadger account is being overwhelmed with noise from some bots. Seeing a lot of Bandit.HTTPError...
New
samoloth
Hi, I’ve just set up an application with ash_authentication. There is only magic link strategy for now, so there is no confirmation add o...
New
velrest
So my question is quite simple and i have found no conclusive answer on forum, google or AI. Should we use :erlang.float for Integer to ...
New
FlyingNoodle
If a change or preparation module uses Ash.Changeset.get_argument/2 or Ash.Query.get_argument/2 (or any of the other get_argument functio...
New

Other Trending Topics Top

mudasobwa
I am happy to introduce the very α version of the new programming language compiled to BEAM. Welcome Cure. It has literally three kille...
New
garrison
Hobbes is a low-level distributed database for the Elixir programming language. Hobbes provides a simple, safe, and scalable storage lay...
New
mcass19
ExRatatui lets you cook up rich terminal UIs in Elixir, powered by Rust’s ratatui via Rustler NIFs. Build interactive terminal applicatio...
New
Damirados
Hello everyone. After busy few months I am happy to announce v0.1.0 of Emerge &amp; Solve. They are GUI (Emerge) and State management (S...
New
netoum
Corex is an accessible, unstyled UI component library for Phoenix that integrates Zag.js state machines using Vanilla JavaScript and Live...
New
wintermeyer
There are three potential reasons for members of this forum to have a look at https://vutuv.de You are tired or annoyed of LinkedIn. Yo...
New

We're in Beta

About us Mission Statement

Options

Thread Display Mode




Thread Preview

Skip Thread Previews