alex88
Compilation order during tests
I’m trying to mock ExTwitter during tests, I’m using GitHub - dashbitco/mox: Mocks and explicit contracts in Elixir · GitHub for that.
In my code I use a config variable to do the Dependency Injection, like this:
config :my_app,
# DI
twitter_client: MyApp.TwitterClient
I’ve defined my mock in test/support/mocks.ex as per Mox docs like this
Mox.defmock(MyApp.Mocks.TwitterClient, for: MyApp.TwitterClient)
and in my test.exs config I have
config :my_app,
# DI
twitter_client: MyApp.Mocks.TwitterClient
Everything worked while incrementally compiling during development, however on a fresh checkout without any _build folder i get this error:
== Compilation error in file test/support/mocks.ex ==
** (ArgumentError) module MyApp.TwitterClient is not available, please pass an existing module to :for
lib/mox.ex:92: Mox.validate_behaviour!/1
lib/mox.ex:84: Mox.defmock/2
(elixir) lib/kernel/parallel_compiler.ex:121: anonymous fn/4 in Kernel.ParallelCompiler.spawn_compilers/1
if I remove that mock line, compile and add the line back everything works. It seems like it’s trying to compile the mock file first and then the lib folder.
My elixirc_paths is ["lib", "test/support", "test/factories"] so lib comes first, any idea?
I’ve pushed a sample app with the problem in https://github.com/alex88/myapp just run mix test
Marked As Solved
ericmj
If you call Mox.defmock at compile time, which you do if you put it in the body of a file covered by elixirc_paths, then the function will call Code.ensure_loaded? at compile time. You will see that if you follow the stacktrace leading up to mox/lib/mox.ex at main · dashbitco/mox · GitHub. Calling Code.ensure_loaded? at compile is not safe because it’s a race to check if the module is loaded. If you instead call Code.ensure_compiled? then the compiler will wait until the given module is compiled, it works similar to require. You will notice that it works if you add a call to Code.ensure_compiled? before the call to Mox.defmock.
Also Liked
hubertlepicki
I figured it out why it works for me. I have 3 apps in umbrella, and I only mock the interactions where one calls another. So I suppose in my case the clean build works since the app I am about to mock is already always fully compiled (is in_umbrella dependency).
hubertlepicki
ericmj
Yes, modules will be compiled in the correct based on the calls or requires that is made in compile time. If you call a module at compile time the calling module will wait until the callee is compiled.
If it’s supposed to be supported to define behaviour implementations* at compile time then I think this line mox/lib/mox.ex at main · dashbitco/mox · GitHub should call Code.ensure_compiled? instead.









