ArthurMmn
Conflicts between Mox, behaviours and optionnal arguments
I have a behaviours with lots of functions. Each pass an arguments and an optional keyword list of options. And I found myself in this weird situation where testing with Mox introduce some unsatisying results.
A reduction of the problem :
defmodule MyBehaviour do
@callback greet(name :: String.t(), greeting :: String.t()) :: String.t()
end
defmodule MyModule do
@behaviour MyBehaviour
@impl true
def greet(name, greeting \\ "Hello") do
"#{greeting}, #{name}!"
end
end
And a test like this
import Mox
defmock(MyMock, for: MyBehaviour)
MyMock
|> expect(:greet, fn name -> "Hello #{name}" end)
result = MyMock.greet("World")
It fails because the mock does not know any function of arity 1 called greet.
** (ArgumentError) unknown function greet/1 for mock MyMock
(mox 1.2.0) lib/mox.ex:681: Mox.add_expectation!/4
(mox 1.2.0) lib/mox.ex:549: Mox.expect/4
If I test instead
import Mox
defmock(MyMock, for: MyBehaviour)
MyMock
|> expect(:greet, fn name, _ -> "Hello #{name}" end)
result = MyMock.greet("World")
It obviously fails, because the function with one argument is never mocked.
** (UndefinedFunctionError) function MyMock.greet/1 is undefined or private. Did you mean:
* greet/2
MyMock.greet("World")
Which leaves me with two choices (I think) :
- Duplicating all callback with and without the optional arguments (sad when there is a lot of callbacks)
- Removing the “optional” from the arguments and always calling the two arity functions.
I chose option 2 with a lack of enthusiasm, anyone in the same situation chose a different solution ? Or had a better way of dealing with the situation ?
Most Liked
LostKobrakai
Optional parameters are a compile time construct of elixir. Behaviours are an erlang level feature, which existed even before elixir existed. Generally I’d keep the interface to multiple implementations as simple as possible and handle the optional stuff before or after that interface.
garrison
Expanding on the above, default args are not actually “real”. They are just syntax sugar for additional function heads.
I suppose that sugar could be extended to callbacks, though it might be a bit confusing to generate multiple callback “heads”. The abstraction starts to leak.
And who defines the default arg’s value? The callback, or the implementation? It’s messy.
funboy
you can try to use GitHub - edgurgel/mimic: A mocking library for Elixir · GitHub
there is no mess with creating behaviours only for testing purposes ![]()
Popular in Questions
Other popular topics
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
- #channels
- #elixirconf
- #exunit
- #discussion
- #code-sync
- #javascript
- #podcasts
- #onsite
- #dialyzer
- #docker
- #authentication
- #umbrella
- #full-time-contract
- #podcasts-by-brainlid
- #ecto-query
- #elixir-ls
- #phoenix_html
- #iex
- #blog-post
- #graphql
- #genstage
- #ai
- #websockets
- #supervisor
- #advent-of-code
- #elixirconf-us
- #distillery
- #processes
- #forms
- #api
- #metaprogramming
- #security
- #performance










