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?
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.