Enum.each теряет переменную при циклическом тесте ExUnit - PullRequest
1 голос
/ 29 мая 2019

При выполнении следующего кода я получаю предупреждение:

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

, за которым следует неудачный тест:

== 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

Кажется, что в блоке Enum.eachзначение char определено для строки test "... #{char}" do, но становится неопределенным в пределах утверждений.

Почему это происходит?

1 Ответ

3 голосов
/ 29 мая 2019

ExUnit.test/3 - это макрос , который определяет функцию для вас.

Каждый раз, когда вы определяете новую функцию в Elixir, она запускает новую область. Это означает, что любая переменная, определенная вне функции, не будет доступна внутри функции. Например, вы не можете сделать это:

foo = 1
def some_function() do
  foo
end

Есть несколько способов, которыми вы можете обойти этот механизм. Одним из них является использование unquote для ввода некоторых значений в качестве AST. Однако в этом случае самый простой подход - поместить значение в атрибут модуля, чтобы вы могли прочитать его внутри функции:

@foo 1
def some_function() do
  @foo
end

Если вы хотите запустить один и тот же тест с разными входами (хотя это, вероятно, означает, что существуют проблемы с дизайном структуры теста), вы можете использовать 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

Атрибуты модуля, очевидно, видны из любого места в модуле после их определения.

...