Testing phoenix controller without an endpoint

Currently I am building a boilerplate for kickstarting my projects with stuff I’m doing over and over again.

The structure is basically an umbrella project which contains a main phoenix application (without ecto) and a number of applications for dealing with the database, authentication, mailing, file uploads, etc. etc.

Since I am severly allergic to boring, repetitive work I want the authentication project to contain pre manufactured views and a ready made setup for the router to use Ueberauth.

So I built an AuthWeb.AuthController which is a Phoenix.Controller that plugs plug(Ueberauth). Then I have created a macro which wraps the route definitions and does some voodoo to apply a specified container view to my pre made templates. This can be imported in the main phoenix application with one simple call inside the router.

All of this is working just fine except for one thing: tests.

I cannot for the live of me figure out how to properly test this setup in isolation.
So this is the most basic testcase I’d like to have

  test "gets a registration form" do
    conn =
      ConnTest.build_conn()
      |> get("/auth/indentity")
      |> Phoenix.Controller.put_view(AuthWeb.AuthView)
      |> AuthController.login(%{})

    assert conn.resp_body =~ "Sign up"
  end

Since I do not want to spin up a whole phoenix application for this use case, I just collected what I really need and set up the whole project by hand. This also means that I do not configure a Phoenix.Endpoint which would be necessary to run this test above. I don’t want to do that! The whole setup is meant to completely ignore the outer world. It should only consist of functions and macros I call from other applications and not spin up a whole webserver with pubsub and live reloading.

So the question is: is there a way to mock the Endpoint only for tests in order to be able to utilize all the phoenix features I want (csrf protection, templating, easy integration with ueberauth), or do I have to bite the bullet and refactor my auth app to use Plug only without any phoenix magic?

1 Like

Maybe you can test with Plug.Test instead of phoenix’s testing module? I don’t think it requires an @endpoint set up.

Hey thanks for chiming in. :wave:

Yeah using Plug.Test was my first strategy, but it turns out that when you want to use Phoenix.Controller you need some dependencies in place. I honestly don’t recall exactly what the problem was, but the solution that I found was basically: just use Phoenix.ConnTest instead.

Actually you were right @idi527. It was the simplest path to go with Plug.Test and work through the things enpoint is providing and stubbing those by hand.

In a github issue José Valim linked to the definition of the Plug.Cookies tests: https://github.com/elixir-plug/plug/blob/master/test/plug/session/cookie_test.exs#L31-L35

So my code now looks like:

defmodule AuthWeb.AuthControllerTest do
  use ExUnit.Case, async: true
  use Plug.Test

  alias AuthWeb.AuthController

  alias Plug.Session.COOKIE, as: CookieStore

  @default_opts [
    store: :cookie,
    key: "foobar",
    encryption_salt: "encrypted cookie salt",
    signing_salt: "signing salt",
    log: false
  ]

  @signing_opts Plug.Session.init(Keyword.put(@default_opts, :encrypt, false))

  test "gets a registration form" do
    conn =
      conn(:get, "/auth/identity")
      |> Plug.Session.call(@signing_opts)
      |> fetch_session()
      |> fetch_query_params()
      |> Phoenix.Controller.put_view(AuthWeb.AuthView)

    conn = AuthController.login(conn, %{})

    conn.resp_body =~ "Welcome!"
  end
end

Which currently is all I need. :+1: It seems to be a little hackish over all and obviously I do not need all options provided above, but I think the idea gets clear.

Nevertheless: the problem is solved.

Are you open-sourcing this, by the way? It sounds useful.

1 Like

That a definitive maybe! :wink:
I think I will eventually if it proves to be indeed useful and not more work in the end than simply building a monolith.

In theory it should be cool, but it might turn out to be completely bonkers in production or killing productivity for real world applications. But if it proves to be successful for our internal use I think I will make it open source. :slight_smile:

1 Like