【问题标题】:Is there a way to test private functions in modules in ExUnit of Elixir?有没有办法在 Elixir 的 ExUnit 中测试模块中的私有函数?
【发布时间】:2020-01-25 21:22:47
【问题描述】:

defp 定义的函数未导出,因此我无法在模块以外的地方执行它们。

【问题讨论】:

  • 我想知道是否可以编写一个宏,在 DEV 和 PROD 混合环境中扩展为 defp,在 TEST 环境中扩展为 def
  • @TalkLittle 是的,这是可能的。请在下面查看我的答案。

标签: elixir


【解决方案1】:

不,没有办法通过 ExUnit 测试它们。

我个人避免测试私有函数,因为通常您最终会测试实现而不是行为,并且一旦您需要更改代码,这些测试就会失败。相反,我通过公共函数测试预期的行为,将它们分成小的、一致的块。

【讨论】:

  • 仅供参考,无论语言或测试库如何,这都是个好建议。
  • 我倾向于不同意:有两种测试方法。在行为上
  • 抱歉,编辑此内容被打断。全球一致,但认为某些私有函数可以从测试中受益,以进行重构。
  • @tompave 我猜你在某些情况下是对的,但是我想为我的图书馆用户提供对该实用程序模块的受限访问,但这似乎有点复杂。还是觉得重构用例被低估了。
  • @lab419 您可以将@moduledoc false 添加到模块中以将其及其功能从文档中排除。这些函数仍然可以调用。我有点在您的偏好和将功能公开的想法之间摇摆不定。从某种意义上说,它们并不有趣,想要测试函数但不将它们作为应用程序或库的公共 API 的一部分公开似乎是合理的。也许,在实践中,证明一个模块或函数不是“公共 API”的一部分就足够了,即可能会被删除或其他“重大更改”。
【解决方案2】:

在您的模块定义中,您可以使用@compile 指令在测试环境中导出您的私有函数。

defmodule Foo do
  @compile if Mix.env == :test, do: :export_all

  # This will be exported for tests
  defp bar() do
  ... code ...
  end
end

【讨论】:

  • 更新:这将在 Elixir 1.5 中发出警告,因此它可能不是最好的方法。
【解决方案3】:

可以使用宏根据环境更改函数的可见性:

defmacro defp_testable(head, body \\ nil) do
  if Mix.env == :test do
    quote do
      def unquote(head) do
        unquote(body[:do])
      end
    end
  else
    quote do
      defp unquote(head) do
        unquote(body[:do])
      end
    end
  end
end

然后您可以将函数公开给您的测试,如下所示:

defp_testable myfunc do
  ...
end

出于何塞的回答中给出的原因,我建议谨慎使用它。它不能替代测试模块的外部行为。不过,它在某些情况下可能很有价值。

(Source)

【讨论】:

  • @TalkLittle 确实如此。宏只影响可见性,并保持参数/正文不变。
  • 这不是一个好主意,因为当私有函数被错误调用时,这不会检测到潜在的错误。
  • @lab419 这是一个中肯的批评。该技术不能替代其他检测私有函数不当使用的测试方法,应谨慎使用。不过,正如您和其他人所指出的,这对于重构和集成测试场景很有用。
  • @acj 抱歉,如果我不清楚,如果您根据混合环境更改函数的隐私,那么您测试的只是测试混合环境。您不再测试 dev 或 prod 环境,因为它不包含相同的代码。更糟糕的是,您会得到误报,因为您的测试环境中将有公共功能,而这些功能在其他地方是私有的。 HTH
  • @lab419 我明白,我同意你的看法。 :) 任何选择使用它的人都应该小心,并且应该有一个额外的机制来测试开发/生产环境。
【解决方案4】:

不能从其模块外部调用私有函数,即使在使用 ExUnit 进行测试的上下文中也是如此。

其他人建议使用复杂的方法来公开私有函数以进行测试,因此我建议使用其他两种看起来更简单的方法:

1) 将要测试的函数设为公开,但用@doc false 标记,这意味着它不是模块的公共 API 的一部分。

2) 或者,如果您要测试的函数是 defp foo,则在同一模块中创建一个 def test_foo,该模块采用您希望在测试期间改变的参数,使它们适应 foo 的期望,最后调用foo。您也可以只在这样的测试期间生成您的特殊测试函数

  if Mix.env == :test do
    def test_foo ...
  end

这些替代方案中的任何一个都更简单,并且在您的项目中不需要额外的依赖项。

当然,正如其他人提到的,一般规则是避免测试私有函数,但有时测试它们是简化测试并提高测试覆盖率的方法。

编码不错!

【讨论】:

    猜你喜欢
    • 2023-03-15
    • 2011-08-07
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2020-08-31
    • 2015-04-24
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多