robbplo
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.
Trending in Questions
Hey guys,
I’ve got a huge CSV ( around 10 GB ) that needs to be processed hourly
Do you guys have any suggestions what is the best prac...
New
Hello!
Could someone please give me a help/sample code, how to delete a file from s3 using waffle/waffle_ecto from Phoenix app.
I creat...
New
I have what I’ve heard referred to as a “lookup table” in my database. This is a way of assigning codes to common values. One common lo...
New
What approach to take when sending live updates to “random” users Hi! I have a question, I have a little chat app, and when I create a DM...
New
Anyone here using Honeybadger?
My Honeybadger account is being overwhelmed with noise from some bots. Seeing a lot of
Bandit.HTTPError...
New
I’m seeing that a list inside a Kino.DataTable will be interpreted as a charlist, even if the Kino.configure() is set to charlists: :as_l...
New
Hi, I’ve just set up an application with ash_authentication. There is only magic link strategy for now, so there is no confirmation add o...
New
Other Trending Topics
Hobbes is a low-level distributed database for the Elixir programming language.
Hobbes provides a simple, safe, and scalable storage lay...
New
ExRatatui lets you cook up rich terminal UIs in Elixir, powered by Rust’s ratatui via Rustler NIFs. Build interactive terminal applicatio...
New
Hello everyone. After busy few months I am happy to announce v0.1.0 of Emerge & Solve.
They are GUI (Emerge) and State management (S...
New
Corex is an accessible, unstyled UI component library for Phoenix that integrates Zag.js state machines using Vanilla JavaScript and Live...
New
There are three potential reasons for members of this forum to have a look at https://vutuv.de
You are tired or annoyed of LinkedIn.
Yo...
New
Aludel - LLM Evaluation Workbench
Aludel is an embeddable Phoenix LiveView dashboard for evaluating and comparing LLM prompts across mult...
New
Categories:
Sub Categories:
Forums
Popular Tags
- #ecto
- #liveview
- #troubleshooting
- #learning-elixir
- #deployment
- #library
- #erlang
- #testing
- #genserver
- #mix
- #absinthe
- #remote-other
- #otp
- #plug
- #how-to-question
- #macros
- #postgres
- #elixirconf
- #channels
- #exunit
- #discussion
- #code-sync
- #javascript
- #podcasts
- #onsite
- #dialyzer
- #docker
- #authentication
- #umbrella
- #full-time-contract
- #podcasts-by-brainlid
- #ecto-query
- #blog-post
- #elixir-ls
- #ai
- #elixirconf-us
- #phoenix_html
- #iex
- #graphql
- #genstage
- #websockets
- #supervisor
- #advent-of-code
- #distillery
- #processes
- #api
- #forms
- #hex
- #security
- #metaprogramming











Showing Posts 1 to 6- Show Best Posts
- Show All (oldest first)
- Show All (newest first)
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()returnMyImplementationstatically or correctly hardcode the typespec return value of it asMyImplementationthen dialyzer can help you. But loosening the type information to not return a specific module means dropping to anymodule(), which is an alias foratom(). 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.
robbplo
I realize that the compiler cannot know which module is being used at runtime, but i was hoping that returning a reference to the behaviour in the spec would give dialyzer the necessary information. The spec info is contained in the behaviour after all, which doesn’t change at runtime.
The
compile_envexample does indeed work, thanks for that tip! I often use mocks in some tests and the real implementation in others, so this isn’t ideal for me. I suppose it would be possible to change the module attribute at runtime, but that feels like it goes against the whole point of using Mox.Second solution
Another possible solution could be to move the dynamic resolution into
MyBehaviour:There is some more duplication, and something about adding this code into the behaviour itself feels like a crime of code quality, but I think it would get the job done for me.
Third solution
Another thing i tried which seems preferable, but does not work, looks like this:
Add another implementation which uses the dynamic module. Does not give an error, even though I’m calling it statically as
MyDispatcherSolutionThree.get_integer(). It works only when i explicitly copy the spec:Doesn’t
@impl MyBehaviourSolutionThreealso inherit the spec information?LostKobrakai
Mox.stub_withcan help there.robbplo
I only considered
stub_withto have some default functionality in the mock… Seems like a great idea to stub with an actual implementation, thanks!Gustaw
I have a similar issue, but it’s actually the other way around. There is a client with a stubbed implementation or a real one being chosen at compile time. The stub is for testing/dev and always succeeds returning an
{:ok, Res}tuple. The real implementation may fail with{:error, Error}. Since Dialyzer is run with the:testenv, it returnsThe pattern can never match the type.in places in which the caller handles error return values. I want to avoid running Dialyzer in:prodenv, and I’m not sure how to deal with this issue.LostKobrakai
You could go with the approach the elixir codebase takes: