Elixir / ExUnit:可以将上下文从测试用例传递到拆解/清理方法(on_exit)吗?

时间:2017-03-06 11:04:02

标签: unit-testing functional-programming elixir teardown ex-unit

问题

我想测试与主机系统交互的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是一个非常愉快和完美的体验!

2 个答案:

答案 0 :(得分:2)

在这种特殊情况下,我只会在你的设置功能(你的第三个解决方案)中注册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作为基本目录

答案 1 :(得分:0)

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

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 docs中所述:

  

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