背景
我有一个伞式项目,我从根运行
mix test
。
在其中一个应用程序中,我使用 Mock 库模拟 File 模块。
问题
这里的问题是,当我运行
mix test
时,进程终止,没有显示错误消息:
Manager.Impl.Store.ReaderTest [test/unit/store/reader_test.exs]
* test list_syndicates/1 returns the list of all known syndicates [L#496]** (EXIT from #PID<0.98.0>) killed
代码
测试的代码如下:
defmodule Manager.Impl.Store.ReaderTest do
use ExUnit.Case, async: false
alias Manager.Impl.Store.Reader
import Mock
setup do
%{
file_io: File,
paths: [syndicates: ["syndicates.json"]]
}
end
describe "lists syndicates" do
test_with_mock "returns the list of all known syndicates", %{paths: paths} = deps, File, [],
read: fn _filename -> {:ok, "[\"utc\"]"} end do
# Act
actual = Reader.list_syndicates(deps)
expected = {:ok, [%Syndicate{name: "UTC", id: :utc, catalog: []}]}
expected_path = Path.join(paths[:syndicates])
# Assert
assert actual == expected
assert_called(File.read(expected_path))
end
end
end
相比之下,以下测试(不使用模拟)效果很好:
defmodule Manager.Impl.Store.ReaderTest do
@moduledoc false
use ExUnit.Case, async: false
alias Manager.Impl.Store.Reader
import Mock
setup do
%{
paths: [syndicates: ["syndicates.json"]]
}
end
describe "list_syndicates/1" do
defmodule FileMockListSyndicates do
@moduledoc false
def read(path) do
assert path == "syndicates.json"
{:ok, "[\"utc\"]"}
end
end
setup do
%{
file_io: Manager.Impl.Store.ReaderTest.FileMockListSyndicates
}
end
test "returns the list of all known syndicates",
%{paths: paths} = deps do
# Act
actual = FileSystem.list_syndicates(deps)
expected = {:ok, [%Syndicate{name: "UTC", id: :utc, catalog: []}]}
# Assert
assert actual == expected
end
end
end
对我来说,这相当令人惊讶。一种替代方案会导致进程崩溃且没有错误消息,而另一种方案则可以使一切正常工作。
对我来说,这表明了三个问题之一:
我相信第二个和第三个选项是最有可能的,但没有任何有关错误的信息,我无法确定。这个进程就直接死掉了。
问题
为什么我的进程死掉了,我该如何修复它?
当您以这种方式模拟模块时,只允许调用
File.read/1
。如果您的代码在任何地方调用 File
模块中的另一个函数,它将失败。要更改此行为,您可以使用 :passthrough 选项。
此外,出于这个原因,模拟标准库函数并不是一个好主意,因为你永远不知道它们在哪里被调用。您可以将
File.read/1
调用包装在另一个函数中,并模拟此函数。