ExUnit anti pattern?

I wanted to know if this is a good patter for setting up a context in ExUnit with phoenix?

This test is using ConnCase so from the looks of it, the ConnCase setup runs first and then is followed by the setup inside the test file.

Thoughts on how to improve this pattern?

defmodule Opalnova.PostControllerTest do
  import Opalnova.Factory #note using ex_machina via insert(:post)
  use Opalnova.ConnCase

  ...
  
  setup context do
    case context do
      %{with_post: true} ->
        {:ok, %{conn: context.conn, post: insert(:post) }}
      _ ->
        :ok
    end
  end

  test "lists all entries on index", %{conn: conn} do
    conn = get conn, post_path(conn, :index)
    assert html_response(conn, 200) =~ "Listing posts"
  end

  ...

  @tag :with_post
  test "renders form for editing chosen resource", %{conn: conn, post: post} do
    conn = get conn, post_path(conn, :edit, post)
    assert html_response(conn, 200) =~ "Edit post"
  end

end

the {:ok, %{conn: context.conn, post: insert(:post) }} feels dirty

2 Likes

I don’t think there’s anything inherently wrong with it—its an interesting technique I haven’t seen before, actually, and I can see that it might be useful sometimes. But in this case, I think you’re better off just duplicating the insert(:post) in the tests that need it rather than being clever with tags. If you find that the duplication gets more complex, i would probably split my test into two different files where each test in the file uses the same setup scenario. The cognitive load in connecting the tag with your setup is kind of high for what I see as a small savings.

Just my two cents, YMMV…

3 Likes

I agree with your assessment and maybe my example is contrived.

To give you a little more back ground, I’m coming from ruby + rspec and I was looking for something comparable to rspec’s let + context block. I wanted to find a way where setup could be identifiable as only useful in a given context.

1 Like

Yeah, coming from a heavy Ruby background myself I can see where you were coming from. I think you’ll probably find that in the general consensus in Elixir is that rspecs let and nested contexts do more to detract from maintainability than they help, so they’ve been left out by intention.

What you might try instead of tag-based setup is to collect repetitive setup steps into a set of functions you can call to quickly init a given scenario, leaning less on the setup context. Those can also be moved into an imported module for use across tests if needed.

2 Likes