Closed XelaRellum closed 5 years ago
You are mocking the File
module in your tests, which is a core elixir module.
If you try mocking something else (e.g. FileEx
), the error you quoted does not appear to reproduce.
I don't know the inner implementation details of umbrella projects, but I suspect it may be using the FIle module. Your mock interferes with the expected behaviour of core modules causing it to crash.
There's no real workaround other than re-thinking the test. I would create a wrapper module for File
and mock that instead.
Tried this, but not the File
module seems to be the problem, but the Agent
that is used to carry state (and by the way the File
mock should not escape the with_mock
statement, anyway).
I have updated the project and included a subproject with_umbrella_and_file_wrapper
.
I think I'm misunderstanding something. The with_umbrella_and_file_wrapper
project has the following mock:
test "unsuccessful read", mocks do with_mock(File, [ write!: mocks.write!, read!: fn _path -> "" end ]) do assert FileEx.write_and_verify("some path", "som content") == false end end
I do not suggest you mock core Elixir libraries unless you're 100% it won't have any side effects on the rest of the system, which in this case, it seems to have.
You are right. When replacing File
with FileWrapper
I left this one location unchanged and this caused the bug.
For me, the lesson learned is not to mock core Elixir libraries.... Thanks for the help!
If anyone's interested what is happening under the hood, I dig a bit into it and here's a short explanation:
To mock a module, meck loads some synthetic code instead of the real module, and to unmock it, the real module is loaded again. Erlang can only keep two versions of any module in memory, so the second reload kills all processes that were running the original code. And mix happens to run old File code for longer periods of time: https://github.com/elixir-lang/elixir/blob/98485daab0a9f3ac2d7809d38f5e57cd73cb22ac/lib/mix/lib/mix/project.ex#L355
The crash can be reproduced by this short iex snippet:
iex(1)> File.cd!(".", fn -> :code.purge(File); :code.load_file(File); :code.purge(File); :code.load_file(File) end)
** (EXIT from #PID<0.127.0>) shell process exited with reason: killed
(the second :code.load_file
is redundant, it's the second :code.purge
what kills it, it's just there for readers to understand what two reloads look like)
I have an umbrella project which contains a subproject that uses
Mock
andAgent
to mock outFile
functions. The same code works fine in a project that is not part of an umbrella project.But when I run
mix test
for the umbrella project, the ExUnit runner gets killed with an output like this:** (EXIT from #PID<0.92.0>) killed
.I have created a repository to reproduce this behaviour here: https://github.com/XelaRellum/mock_bug_with_umbrella
Thanks a lot for you help and for creating Mock!