robbplo

robbplo

Dialyzer: inferring types when using Mox

I ran into this issue the other day and it’s been bugging me ever since. Below is a basic behaviour/implementation, and function which returns a module at runtime. This is the recommended way to decouple your modules when using Mox. It seems to me that this breaks Dialyzer analysis, since no errors are produced with this clearly incorrect typing.

defmodule MyBehaviour do
  @callback get_integer() :: integer
end

defmodule MyImplementation do
  @behaviour MyBehaviour

  @impl MyBehaviour
  def get_integer, do: 100
end

defmodule DialyzerMoxBehaviours do
  @moduledoc """
  Using the recommended implementation of a behaviour for Mox, Dialyzer is unable to infer the type of `get_integer()`.
  By decoupling this module from `MyImplementation`, we lose the typespec information from the behaviour.
  """

  @spec dialyze_me() :: binary
  def dialyze_me, do: "hello" # valid type

  @spec dialyze_me_two() :: binary
  def dialyze_me_two, do: impl().get_integer() # invalid type, but dialyzer does not complain.

  @spec impl() :: MyBehaviour
  defp impl(), do: Application.get_env(:dialyzer_mox_behaviours, :impl, MyImplementation)
end

Am I missing something? I like using Mox but this seems like a major drawback to me. If anyone else has encountered this issue I’m interested in how you’ve solved it.

Marked As Solved

LostKobrakai

LostKobrakai

Mox.stub_with can help there.

Also Liked

LostKobrakai

LostKobrakai

The approach as stated does loose type information, but not because of mox or behaviours, but because of the runtime selection of the used implementation. No static analysis can catch errors based on information not available statically in the code.

If you make impl() return MyImplementation statically or correctly hardcode the typespec return value of it as MyImplementation then dialyzer can help you. But loosening the type information to not return a specific module means dropping to any module(), which is an alias for atom(). There’s nothing to check if the returned module is unknown.

The typesystem dialyzer uses doesn’t allow you to say “but the module returned implements that behaviour”. It’s either one specific module or any module for that typesystem.

Given the typesystem doesn’t help you can however go the “statically known” route. If you don’t need runtime selection you can compile the configured implementation into the module statically.

@impl Application.compile_env(:dialyzer_mox_behaviours, :impl, MyImplementation)
@spec impl() :: module()
defp impl(), do: @impl

Where Next?

Popular in Questions Top

vonH
In asking this question I am more interested about the expressiveness of the language itself and less concerned about the availability of...
New
lessless
I believe there are people here who are dealing with CSV files import on the daily basis, and since Excel is a really popular tool there ...
New
LegitStack
I’m hoping you guys can give me some general advice and perhaps code examples if you’re feeling up to it. I’m very interested in Elixir,...
New
itssasanka
Hi all, Trying to get some more clarity over utc_datetime and naive_datetime for Ecto: https://hexdocs.pm/ecto/Ecto.Schema.html#module-...
New
myronmarston
The Elixir Typespec docs show the following syntax for keyword lists in typespecs: # ... | [key: type] # keyword lis...
New
fireproofsocks
Forgive me if this is obvious, but how does one delete a database record WITHOUT selecting it first? https://hexdocs.pm/ecto/Ecto.Repo.h...
New
baxterw3b
Hi guys, i’m new in the Elixir world, and i have to say, that i love it! i’m having some problem to understand anonymous functions with ...
New
Patoshizzle
After calling mix ecto.create I get this error: 17:00:32.162 [error] GenServer #PID<0.412.0> terminating ** (Postgrex.Error) FATAL...
New
Exadra37
Sometimes I want to check if the input into a function is not a blank string. My first approach: defmodule Example do def do_stuff(s...
New
lucidguppy
I have a super simple question about elixir - how would I take a file like this foo bar baz and output a new file that enumerates th...
New

Other popular topics Top

senggen
Erlang/OTP 25 [erts-13.2.2] [source] [64-bit] [smp:8:8] [ds:8:8:10] [async-threads:1] 15:22:35.803 [error] gen_event {lager_file_backend...
New
dotdotdotPaul
Okay, I'm having a heck of a time trying to figure out how to best handle the validation of belongs_to associations in Ecto. I'm sure I'...
New
lessless
I believe there are people here who are dealing with CSV files import on the daily basis, and since Excel is a really popular tool there ...
New
malloryerik
Hi, this is for people who, like me, have had some friction using .html.heex templates in VSCode. The solution seems to be, in a hyphena...
New
quazar
How to set Jason to encode all fields in ecto schema, I don’t care about security and implementing only is taking long list of attributes...
New
fireproofsocks
I’m working on defining a simple Ecto schema for a table (in PostGres), but I don’t see where I can define a column as NOT NULL. Conside...
New
lastday4you
I wanted to check elixir version in phoenix because i found that my elixir is 1.5 but when i use Enum.chunk_by it said the function is un...
New
script
If I have a string “1000 cfu/ml” . I want to remove the characters and / and space . So the string is like this "1000" What is the ...
New
myronmarston
The Elixir Typespec docs show the following syntax for keyword lists in typespecs: # ... | [key: type] # keyword lis...
New
alice
Hey, Just curious what are the main benefits of Elixir compared to Clojure? When is Elixir more useful than Clojure and vice versa? Th...
New

We're in Beta

About us Mission Statement