How to configure build_conn() to be domain aware for the testing


I am making a routing to be aware of a subdomain like the following.

scope "/", ProjectWeb, host: "management." do
# Use the default browser stack
pipe_through([:browser, :management])

get("/", ManagementController, :index)
scope "/management", Management do
  resources("/papers", PaperController, as: "manage_paper")

And I am having different plugs like the above one.

I am wondering how to I use build_conn() or any otherways to create a connection for testing in my test suits for each one of my subdomain.

You’d need to set the host to the conn somewhere in your test setup. Could be in the testcase template or by some of the setup callbacks of ex_unit.

Hi @LostKobrakai

The host is set on the endpoint using

# config/test.exs
config :project, Project.Endpoint,
  http: [port: 4001],
  server: false,
  url: [host: "management.", port: 4001]

But anyhow, build_conn() actually doesn’t use this info. Since when trying to do management_path(conn, :index) I am getting ** (KeyError) key :phoenix_endpoint not found in: %{phoenix_recycled: true, plug_skip_csrf_protection: true}

so have to switch to management_path(Project.Endpoint, :index) to make it work in the test.

So I am assuming that I can not use normal build_conn() for this case. I need to do something like build_conn() do but make it aware of the endpoint also and also to allow it to set the initial request domain.

1 Like

The url config is only used for generating urls via the router path helpers. It does not affect incoming requests at all.

Sorry I misstyped. not management_path but `management_url(Project.Endpoint, :index) instead.

Yep u are right _url functions are only used for generating urls

but %Plug.Conn{} is aware of endpoint and any requests data.

This is how I do it (for variable subdomains) in my conn_case.ex template:

  setup tags do
    # [ecto setup]
    conn = setup_conn_with_host(tags[:subdomain] || "www")
    {:ok, conn: conn}

  defp setup_conn_with_host(subdomain) do
    host = "#{subdomain}." <> Endpoint.config(:url)[:host]
    Phoenix.ConnTest.build_conn(:get, "http://#{host}/", nil)

With that I can simply set the subdomain for each test individually via a tag.


Thank u for recommending this way.
I try this way and it works, and I just realize that I need not do anything to the endpoint to do testing per subdomain.

Hi @LostKobrakai,

Thank you for recommending above way to handle this issue.
I was able to inject the subdomain by using this in my test Phoenix.ConnTest.build_conn(:get, "", nil). But further than that I could not manage to pass the subdomain through tags.

I am having following code in my controller test -

defmodule CompanyArchiveWeb.ManagementControllerTest do
  use CompanyArchiveWeb.ConnCase, async: true, subdomain: "app"

  test "GET management_path(conn, :index)", %{conn: conn} do
    conn = get(conn, management_path(conn, :index))
    assert html_response(conn, 200) =~ "Management"

In my test/support/conn_case.ex I am having this in my setup block -

setup tags do
  :ok = Ecto.Adapters.SQL.Sandbox.checkout(CompanyArchive.Repo)
  unless tags[:async] do
    Ecto.Adapters.SQL.Sandbox.mode(CompanyArchive.Repo, {:shared, self()})

  IO.inspect tags # Debug to see tags
  conn = case tags[:subdomain] do
    nil -> Phoenix.ConnTest.build_conn(:get, "http://test-company-archive.local/", nil)
    somedomain -> Phoenix.ConnTest.build_conn(:get, "http://#{somedomain}.test-company-archive.local/", nil)

  {:ok, conn: conn}

When I inspect tags inside the above setup block I am getting this -

  async: true,
  case: CompanyArchiveWeb.ManagementControllerTest,
  describe: nil,
  describe_line: nil,
  file: ...,
  line: 4,
  module: CompanyArchiveWeb.ManagementControllerTest,
  registered: %{},
  test: :"test GET management_path(conn, :index)",
  type: :test

I am curious what is the right way of passing parameters to the setup block above?

Just found out that we can use @moduletag for this.

please ignore above reply. thank again @LostKobrakai