使用模块注释作为方法属性

Using module annotations as method attributes

所以这是一个有趣的问题,我查看了 elixir 中模块属性的文档,即底部的 http://elixir-lang.org/getting-started/module-attributes.html 它提到它们可以用作 ExUnit 中的方法注释。

不幸的是,基本上没有关于如何实现这一点的信息,查看 ExUnit 代码让我迷路了。似乎我需要确定最接近属性的方法来说明它们以某种方式关联(尽管可能是错误的)。

知道我可以从哪里了解这方面的信息吗?

它是这样工作的。查看ExUnit.Case.

的源代码

首先,查看 __using__ 宏,因为当您在测试用例中使用它时,它会首先被调用。特别注意 here

    Enum.each [:ex_unit_tests, :tag, :describetag, :moduletag, :ex_unit_registered],
      &Module.register_attribute(__MODULE__, &1, accumulate: true)

这会注册 @tag 和更多的属性。阅读 Module.register_attribute/3 的文档,您会发现这意味着任何时候调用属性时,该值都会附加到先前属性的列表中。

然后记下 test/3 宏,特别是 here

quote bind_quoted: [var: var, contents: contents, message: message] do
  name = ExUnit.Case.register_test(__ENV__, :test, message, [])
  def unquote(name)(unquote(var)), do: unquote(contents)
end

注意对 ExUnit.Case.register_test/4 的调用。在看,特地here

tag = Module.delete_attribute(mod, :tag)

它获取到这里的标签,然后删除它们。通过拥有标签和测试名称,它调用 (here)

test = %ExUnit.Test{name: name, case: mod, tags: tags}
Module.put_attribute(mod, :ex_unit_tests, test)

它将测试连同标签一起保存在另一个属性中。

最后,请注意这里

  @doc false
  defmacro __before_compile__(_) do
    quote do
      def __ex_unit__(:case) do
        %ExUnit.TestCase{name: __MODULE__, tests: @ex_unit_tests}
      end
    end
  end

函数__ex_unit__/1ExUnit.Runner.run_case/3中被调用以获取每个案例中的测试信息。

你明白了吗?使用一个累积的属性,在你的宏中调用一个函数,它总是获取属性的当前值并清除它,然后用这个值做任何你想做的事情,因为你知道它总是在调用宏时使用。

我希望已经足够清楚了,如果您需要更多解释,请发表评论。

PS。我只是阅读源代码来找出这一点。很高兴知道它是如何工作的。