Elixir/ExUnit:测试用例中是否可以将上下文传递给teardown/cleanup方法(on_exit)?

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):文档中是这样做的,但我不知道如何将任何新的状态/上下文传递给它。它似乎只有在所有上下文已经完全在设置函数中定义的情况下才有用。

也许我过度思考了这个问题...我只是因为遇到了一些不完整的清理和导致无限递归的坏的调试经历(本应该被我的代码捕获,但还没有),所以我只想确保我做正确的事情并且以正确的方式学习。除了这些测试,Elixir到目前为止是非常愉快和完美的体验!

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用作基本目录。


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回调按需注册,通常用于撤消由setup回调执行的操作。on_exit/2还可以带有引用,允许在将来覆盖回调。注册的on_exit/2回调始终运行,而setup和setup_all中的失败将停止所有剩余的setup回调执行。


1
顺便提一下,最好在断言之前注册 on_exit 回调函数,否则无法保证在测试失败的情况下它们会运行。 - Victor Schröder

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接