1

When running the following code I get the warning:

warning: variable "char" does not exist and is being expanded to "char()", please use parentheses to remove the ambiguity or change the variable name
  test/my_module_test.exs:7

Followed by a failed test:

== Compilation error in file test/my_module_test.exs ==
** (CompileError) test/my_module_test.exs:7: undefined function char/0
    (stdlib) lists.erl:1338: :lists.foreach/2
    (stdlib) erl_eval.erl:680: :erl_eval.do_apply/6
    (elixir) lib/code.ex:767: Code.require_file/2
    (elixir) lib/kernel/parallel_compiler.ex:209: anonymous fn/4 in Kernel.ParallelCompiler.spawn_workers/6
defmodule MyModule do
  use ExUnit.Case, async: true
  doctest MyModule

  Enum.each ~w(a b c), fn char ->
    test "test involving #{char}" do
      assert char == char
    end
  end
end

It seems that within the Enum.each block the value of char is defined for the line test "... #{char}" do but becomes undefined within the assertions.

Why is this happening?

kingsfoil
  • 3,795
  • 7
  • 32
  • 56

1 Answers1

5

ExUnit.test/3 is a macro that defines a function for you.

Every time you define a new function in Elixir, it starts a new scope. This means any variable defined outside of the function won't be available inside the function. For example, you can't do this:

foo = 1
def some_function() do
  foo
end

There are some ways you can bypass this mechanism. One is to use unquote to inject some values as AST. However, in this case, the simplest approach is to put the value in a module attribute, so you can read it inside the function:

@foo 1
def some_function() do
  @foo
end

Whether you want to run the same test with different inputs (although it likely means there are issues with the test structure design,) you might use ExUnit.Callbacks.setup_all/2 to setup tests context, or use module attributes as shown in the documentation for ExUnit.Case.test/3

Enum.each ~w(a b c), fn char ->
  @char char
  test "test involving #{char}", ctx do
    assert @char == @char
  end
end

Module attributes are obviously visible from anywhere in a module after they are defined.

José Valim
  • 50,409
  • 12
  • 130
  • 115
Aleksei Matiushkin
  • 119,336
  • 10
  • 100
  • 160