17

问题

我想测试一个与主机系统交互并且具有副作用的方法的 Elixir 模块。对于这个问题并保持简短,假设它是创建几个目录。这些目录当然应该在测试运行后删除,如果测试(很长)由于任何原因(错误的模块代码、错误的测试代码等)失败。

我想知道如何最好/最优雅地解决这个清理步骤。我查看了ExUnit.Callbacks.on_exit/2的文档,但它的示例仅用于设置和简单的拆卸(不涉及传递状态)。我也在网上搜索过,但没有发现任何有用的东西,所以可能是我的想法本身不好 - 我也愿意接受重新定义问题的建议。

例子

defmodule SimpleTest do
  use ExUnit.Case

  setup_all do
    ts = Time.utc_now |> Time.to_string
    {:ok, [timestamp: ts]}
    # paths to be cleaned are not yet known here
  end

  test "first test", context do
    path = "/tmp/dir" <> context[:timestamp]
    assert :ok == SimpleModule.mkdir(path)
    assert :eexist == SimpleModule.mkdir(path)
    # [path] should be passed on to cleanup
  end

  test "second test", context do
    path = "/tmp/dir" <> context[:timestamp]
    path2 = "/tmp/dir2" <> context[:timestamp]
    SimpleModule.mkdir(path)
    SimpleModule.mkdir(path2)
    assert File.exists?(path)
    assert File.exists?(path2)
    # [path, path2] should be passed on to cleanup
  end

  defp cleanup(context) do
    Enum.each(context[:dirs], fn(x) -> File.rmdir(x) end)
  end
end

defmodule SimpleModule do
  def mkdir(path) do
    File.mkdir(path)
  end
end

可能的解决方案?

我现在想cleanup/1在每次测试后添加一个要删除的目录列表的调用。以下想法是我尝试过的事情:

  • 在每个测试结束时直接调用该函数:适用于简单的情况,但如果测试用例无限循环,它将被杀死并且不再进行清理。
  • 在每个测试中使用更新的上下文进行调用on_exit(fn -> cleanup(context) end):这似乎可行,但我无法确定是否推荐它,以及在测试中放置调用的位置(开始/结束)是否有所不同。
  • 调用函数:文档执行on_exit(fn -> cleanup(context) end)setup context操作,但我不知道如何将任何新状态/上下文传递给它。只有在设置函数中已经完全定义了所有上下文时,它似乎才有用。

也许我也多虑了这个问题......我只是在不完整的清理和导致无休止的递归中遇到了一些糟糕的调试经验(这应该被我的代码捕获,但还没有),所以我只想确保我做到了正确的事情并以正确的方式学习。除了这些测试之外,到目前为止,Elixir 是一次非常愉快且完美的体验!

4

2 回答 2

10

在这种特殊情况下,我只需on_exit在您的设置函数中注册回调(您的第三个解决方案)。

不要单独删除路径,而是删除父目录:

@test_dir "/tmp/base_test"

setup do
  File.mkdir(@test_dir)

  on_exit fn ->
    File.rm_rf @test_dir
  end
end

然后@test_dir在您的测试中用作您的基本目录

于 2017-08-22T13:25:38.280 回答
1

您还可以在测试用例中注册要在测试退出时执行的回调,并将其传递给特定路径。

test "first test", context do
    path = "/tmp/dir" <> context[:timestamp]

    on_exit(fn -> cleanup(path) end)

    assert :ok == SimpleModule.mkdir(path)
    assert :eexist == SimpleModule.mkdir(path)
end

test "second test", context do
    path = "/tmp/dir" <> context[:timestamp]
    path2 = "/tmp/dir2" <> context[:timestamp]
    SimpleModule.mkdir(path)
    SimpleModule.mkdir(path2)
    assert File.exists?(path)
    assert File.exists?(path2)
    on_exit(fn -> cleanup(path) end)
    on_exit(fn -> cleanup(path) end)
end

您可以在测试用例的任何时候注册它,它将在测试结束后执行。您也可以使用参考术语注册它们。

ExUnit 文档中所述:

on_exit/2 回调是按需注册的,通常用于撤消设置回调执行的操作。on_exit/2 也可以引用,允许将来覆盖回调。注册的 on_exit/2 回调始终运行,而 setup 和 setup_all 中的失败将停止所有剩余的 setup 回调执行。

于 2018-12-28T11:37:00.603 回答