How are functions scoped within ExUnit describe blocks?

Hi,

I was wondering if anyone could help clarify something for me. This is more due to my lack of understanding than anything else.

Suppose I had a module for testing, with two describe blocks. I declare a function with the same name in both, one with defaults, and one without. Perhaps owing to my OO background, I naively expected that these functions are in different scopes and therefore wouldn’t affect each other:

defmodule Sample.SampleTest do
  ...

  describe "test case A" do
    alias AnotherModule
    def helper_function(attrs \\ %{}) do
      ...
    end
  end

  describe "test case B" do
    alias AnotherModule
    def helper_function() do
      ...
    end
  end
end

However, I am getting an error that helper_function/0 conflicts with defaults from helper_function/1.

I presume that the functions are therefore scoped to the containing module? But then, I also have aliases within the describe blocks which don’t appear to be shared. (If I don’t include the alias, it fails to compile if I call a function from the module). How are these different?

If anyone could help me understand this it would be much appreciated.

AFAIK, neither describe nor test define a new module scope (or class as you’d call it). So what you are trying to do will not work. What’s the problem with having functions with different names at the top scope in the same file?

3 Likes

There’s no problem with it; left to my own devices, I probably would have done just that.

I encountered this sort of pattern when I ran a generator for Phoenix for two models under the same context; not having much experience with the language, I simply thought this was a common pattern and tried to maintain that with the new functions I introduced.

To answer my own question, the function is not “scoped” to the describe, because as dimitarvp mentioned, describe does not define a new module scope.

The alias however, is scoped to the nearest block, and the describe is one such construct, so the alias is not “shared” between the two blocks.

Just trying to understand the fundamentals. I feel like this makes sense, but please feel free to correct me if I’m wrong!

1 Like

@soullpoint There is a way to scope them if the functions are used in setups:

describe "test case A" do
    setup :build_user
    
    @describename elem(@ex_unit_describe, 1)
    def build_user(%{describe: @describename}) do
      [user: %{user_id: 1}]
    end
  end

  describe "test case B" do
    setup :build_user

    @describename elem(@ex_unit_describe, 1)
    def build_user(%{describe: @describename}) do
      [user: %{user_id: 2}]
    end
  end