ArthurMmn

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) :

  1. Duplicating all callback with and without the optional arguments (sad when there is a lot of callbacks)
  2. 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

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

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

funboy

you can try to use GitHub - edgurgel/mimic: A mocking library for Elixir
there is no mess with creating behaviours only for testing purposes :smiley:

LostKobrakai

LostKobrakai

You’re essentially running into the fact that behaviours do not have optional parameters. There’s just fixed arity callbacks. Mox being driven by behaviours inherits that. But you could always place an interface with optional parameters in front of code calling only the “fully arity” behaviour callback implementations. That’s e.g. how ecto does it.

E.g.

defmodule Greeter do
  def greet(name, greeting \\ "Hello") do
    Application.get_env(:myapp, __MODULE__).greet(name, greeting)
  end
end
ArthurMmn

ArthurMmn

I guessed there was not much more to find here.

Any thoughts on why callbacks were never implemented with optional parameters ?

Where Next?

Popular in Questions Top

openscript
Hello! Sorry for this astonishing simple question, but I’m really stuck. I try to set up the intellij-elixir plugin, but I don’t know ho...
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
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
qwerescape
Is there a way to get the call stack or stack trace at any point in the code? Not from exceptions, but an expression that returns how the...
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
electic
Hi, I am new to Elixir. I am trying to use the DateTime component to insert a date into MySQL however the there seems to be no way to fo...
New
kostonstyle
Hi all I want to have a unix time, from the current time plus 1 hour. DateTime.now + 1 hour How to get it in elixir? Thanks
New
sabri
Can someone explain the settings of pool_size of Ecto in config file? and what is the recommend size? Thanks
New
siddhant3030
Hi, I have to write a raw query for one of my project. But till now I have used ecto queries and don’t have much experience writing raw ...
New
joeerl
Hello again - after a longish gap I’ve decided I really must dig into Elixir and see what’s been happening here - so I have a few questio...
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
William
I would like to know that is there any online source for learning Phoenix Framework for building E-Commerce Store? Any advantage on build...
New
aalberti333
As the title describes, I’m trying to run Enum.map() over a list of key/value pairs, where the value is a map. My data looks like this: ...
New
sergio_101
I am VERY much an elixir newbie. I have taken one elixir course and one phoenix course on Udemy. During that course, I saw the instructor...
New
grych
Hi folks, Few months ago I have announced the proof-of-concept of the library to manipulate the browsers DOM objects directly from Elixi...
639 49522 488
New
nsuchy
Hi. I’ve noticed that Windows Powershell has it’s own IEX command and you cannot access Elixir’s IEX due to the conflict. This isn’t a cr...
New
mcarvalho
What is the difference between System.get_env and Application.get_env? For example, what are best practices to use one versus another.
New
ashish173
I am using Ecto timestamps with postgres, I can see the timestamps() use the :naive_dateime but for my use case I wanted to store the ti...
New
stefanluptak
Hello everybody, usually, I use a 29" ultra-wide monitor for VSCode which can easily accomodate explorer (files panel) + file with code ...
New
josevalim
Hi everyone, One of the features added to Elixir early on to help integration with Erlang code was the idea of overridable function defi...
New

We're in Beta

About us Mission Statement