所以这是一个有趣的问题,我在elixir中查看模块属性的文档,即底部的http://elixir-lang.org/getting-started/module-attributes.html,它提到它们可以像ExUnit一样用作方法注释。
不幸的是,基本上没有关于如何实现这一点的信息,并且通过ExUnit查找代码让我迷失了方向。似乎我需要确定最接近该属性的方法,以表明它们以某种方式相关联(尽管可能是错误的)。
我有什么想法可以了解这一点?
答案 0 :(得分:3)
它的工作原理如下。查看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
在ExUnit.Runner.run_case/3中调用函数__ex_unit__/1
以获取每个案例中的测试信息。
你明白了吗?使用累积属性,在宏内部调用一个总是获取属性的当前值并清除它的函数,然后用值做任何你想做的事情,因为你知道它总是在调用宏的时候。
我希望它足够清楚,如果您需要更多解释,请发表评论。
PS。我只是阅读了源代码来找到它。很高兴知道它是如何运作的。