chrisalley

chrisalley

How to describe many contexts in ExUnit without a hierarchy

ExUnit now has describe blocks which is a welcome addition coming from RSpec. In the docs, it states that nested hierarchies of describe blocks are forbidden, and instead developers should “build on top of named setups”. The following example used:

defmodule UserManagementTest do
  use ExUnit.Case, async: true

  describe "when user is logged in and is an admin" do
    setup [:log_user_in, :set_type_to_admin]

    test ...
  end

  describe "when user is logged in and is a manager" do
    setup [:log_user_in, :set_type_to_manager]

    test ...
  end

  defp log_user_in(context) do
    # ...
  end
end

For comparsion, here’s how I might lay out the same test using RSpec:

RSpec.describe UserManagement, type: :model do
  describe "some method" do
    context "when user is logged in" do
      before do
        ..
      end

      context "when user is an admin" do
        before do
          ...
        end

        it ...
      end

      context "when user is a manager" do
         before do
           ...
         end

         it ...
      end
    end
  end
end

The reasoning for ExUnit way is solid - it’s easier to glance at a single describe block in isolation and understand exactly what is going on. However, coming from RSpec, the ability the lay out multiple contexts as described above is useful for covering all scenarios. In particular, if there’s three or four different contexts that need to be checked, including different combinations of each, having a hierarchy can help to lay out all of the different pathways through the code. If the contexts are keep flat, we would end up with very long describe/context strings, e.g. “when user is logged in and user is an admin and user is an author and user is a publisher”.

Since ExUnit doesn’t support nested describe/context blocks, what are some alternative strategies to describe these more complex sets of contexts?

Most Liked

josevalim

josevalim

Creator of Elixir

Since ExUnit doesn’t support nested describe/context blocks, what are some alternative strategies to describe these more complex sets of contexts?

Your first approach is the alternative. Sometimes we get too hung up on reducing duplication and we forget the downsides of trying to “unify” everything, such as coupling and loss of context. ExUnit wants you to define everything in two lines (the describe and setup) because the next person reading the file will have a better understanding of what is happening.

Go back to a project that uses nested describes/context and try to find out what a single test is doing. This is going to be the process:

  1. You will find the test and try to find the outer most describe
  2. Put “user is logged in” (the outermost describe) in your brain stack
  3. Then find and put “and is a manager” in your brain stack
  4. Then find and put “with multiple accounts” in your brain stack
  5. Remove “with multiple accounts” from your brain stack (because that context has ended without reaching your test)
  6. Then find and put “with a single account” in your brain stack
  7. Found the test

It is such a hassle.

The describe is there to describe what your test is doing. Be descriptive.

43
Post #3
chrisalley

chrisalley

Thanks for the detailed response, @josevalim. Having dealt with some complex nested specs, I can certainly relate to the desire for decoupling and explicitness when examining individual tests.

My main concern is how to structure contexts without a literal tree of code. But as @georgeguimaraes suggested, the contexts could be placed into seperate files. This would in practice create a kind of hierarchy, with the folder containing the context files acting as the parent. If required, further subfolders could be created to represent grandparent contexts, creating a tree of folders and files instead of a tree of code. Each test would still have the full description of the context in the describe statement so that the test can be understood in isolation, while the folder structure would represent the structure of the many pathways through the code. I imagine a lot of RSpec tests could benefit from this kind of restructuring too, rather than being placed in one giant file that covers every context that the class being tested can be in.

georgeguimaraes

georgeguimaraes

What do you think about breaking each context (logged in) in its own file?

Where Next?

Popular in Questions Top

9mm
I am constructing a JSON object (map) and I need to conditionally set a field. I’m trying to write proper elixir-way code… and I’m at a l...
New
Harrisonl
We have an ECS cluster with 4 services, where each task joins a single cluster, via discovery ECS discovery service. Currently when I de...
New
marius95
Hello everyone, I try to use an Javascript Event Handler in my root.html.leex file. Therefore I created a function in the app.js file: ...
New
vac
Hi, I’m quite new in Elixir and I’m trying to format a string to a PEM format. I have the certificate value like MIIDBTCCAe2...... and I...
New
ycv005
I have followed this StackOverflow post to install the specific version of Erlang. And When I am running mix ecto.setup then getting fol...
New
nobody
Hi! In PHP: $_SERVER[‘SERVER_ADDR’] - in Elixir? Searched the docs for ip address and the web, no good results. Thanks!
New
ashish173
I am using Ecto timestamps with postgres, I can see the timestamps() use the :naive_dateime but for my use case I wanted to store the ti...
New
bsollish-terakeet
Credo is smart enough to check for (something like) this: assert length(the_list) == 0 with this response: Checking if an enum is empt...
New
JorisKok
I have a server on AWS, and was running a load test using artillery. When looking at the Phoenix dashboard I see the Ports going to 100% ...
New
earth10
Hi, I’m just starting to build a side-project with Elixir and Phoenix and doing some basic test with Elixir alone. What strikes me is th...
New

Other popular topics Top

danschultzer
None of the current solutions worked well for me, so I went ahead and built a user management system from scratch. This project took far...
548 29603 241
New
minhajuddin
I have seen a lot of code which picks the first element from a list using Enum.at(0) instead of List.first. Is there a reason why people ...
New
johnnyicon
Hi all, I’ve just started learning Elixir and Phoenix Framework, so please pardon my n00bness at this stage. I’m trying to use Postgres...
New
chrismccord
This release brings a number of exciting features, including integration with the new Phoenix LiveDashboard and Phoenix LiveView. There h...
New
gausby
I asked this very same question on twitter and got some interesting feedback, but I thought it would be a good question to ask here as we...
1207 39467 209
New
vrod
I am using the Starship cross-shell prompt – it seems pretty nice, but I get some errors: [WARN] - (starship::utils): Executing command ...
New
Emily
I have VueJS GUIs with the project generated using Webpack. I have Elixir modules that will need to be used by the VueJS GUIs. I forese...
New
dblack
I’ve got an issue with an app and I’ve no idea of how to troubleshoot it. I’m hoping someone here might have seen something similar. I p...
New
vegabook
I’m brand new to Phoenix and I have stripped one of the demo applications to the bone. I just want to get an svg up on the screen. Here i...
New
jononomo
For some reason my phoenix channels are working for me in my local dev environment, but as soon as I deploy via Docker, I get a 403 error...
New

Latest on Elixir Forum

Elixir Forum

We're in Beta

About us Mission Statement