循环 ExUnit 测试时,Enum.each 丢失变量

011*_*112 1 elixir ex-unit

运行以下代码时,我收到警告:

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
Run Code Online (Sandbox Code Playgroud)

接下来是失败的测试:

== 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
Run Code Online (Sandbox Code Playgroud)
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
Run Code Online (Sandbox Code Playgroud)

似乎在块内,为该行定义了Enum.each的值,但在断言中变得未定义。chartest "... #{char}" do

为什么会发生这种情况?

Ale*_*kin 5

ExUnit.test/3一个为您定义函数的宏。

每次你在 Elixir 中定义一个新函数时,它都会启动一个新的作用域。这意味着在函数外部定义的任何变量在函数内部都将不可用。例如,您不能这样做:

foo = 1
def some_function() do
  foo
end
Run Code Online (Sandbox Code Playgroud)

有一些方法可以绕过此机制。一种是使用unquote注入一些值作为 AST。但是,在这种情况下,最简单的方法是将值放入模块属性中,以便您可以在函数内读取它:

@foo 1
def some_function() do
  @foo
end
Run Code Online (Sandbox Code Playgroud)

无论您想要使用不同的输入运行相同的测试(尽管这可能意味着测试结构设计存在问题),您都可以使用ExUnit.Callbacks.setup_all/2设置测试上下文,或者使用模块属性,如文档中所示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
Run Code Online (Sandbox Code Playgroud)

模块属性在定义后在模块中的任何位置都明显可见。