# Mocking a function with optional argument

Hello

I’m trying to mock a module using the Mox package. Inside that module I have a behaviour defined:

``````  @callback add(number(), number(), [number()]) :: number
``````

As well as implementation for that behaviour:

``````def add(a, b, rest \\ []), do: [a, b | rest] |> Enum.sum
``````

Now I’m trying to mock the `add/3` function which actually can be called as it would be `add/2` (which I’m actually doing inside the “app” code:

``````    @special_module.add(2, 3)
``````

I tried different `expect`s inside the test like:

``````  defp xxx(_, _, _ \\ []), do: 6

Test.SpecialModuleMock

assert Myapp.hello() == 6
end
``````

but none of my ideas works… as the code tried to call `add/2` this is exactly the error I’m getting:

``````  1) test adds two numbers (MyappTest)
test/myapp_test.exs:9
** (UndefinedFunctionError) function Test.SpecialModuleMock.add/2 is undefined or private. Did you mean:

code: assert Myapp.hello() == 6
stacktrace:
test/myapp_test.exs:13: (test)
``````

I created a repository where I have a sample mocked module that does not have optional arguments:

Then I added 3rd argument, and it’s failing:

It looks like the cleanest way to deal with this issue would be to be able to define optional arguments inside the `@callback` attribute(behaviour defined with optional args) - I couldn’t find a way to do it? Is that even possible?

Otherwise, I’m not sure how to write an `expect` function so that the Mox will honour optionality of 3rd argument?

I can’t be the first person to mock a function with optional arguments I surely need to be missing something simple?

You have defined only the callback with 3 arguments in the behavior. (And there is no way to define a callback with optional arguments)

Having said that, I think one of the ways to achieve what you want is to define a callback for each arity:

``````@callback add(number(), number()) :: number
@callback add(number(), number(), [number()]) :: number
``````

I think the reason is that optional arguments are compile time syntax sugar that expand to another function clause.
As this:

``````def add(a, b, rest \\ []), do: [a, b | rest] |> Enum.sum
``````

Compiles into the equivalent of

``````def add(a, b), do: add(a, b, [])
def add(a, b, rest), do: [a, b | rest] |> Enum.sum
``````

If I’m correct.

2 Likes

Thanks for responding. I think you have a point regards compiling into two clauses.

It’s interesting as this example is based on Ecto.Repo which provides @callbacks for all arguments (including optional ones), but functions without optional arguments aren’t part of the behaviour. Maybe that’s an opportunity for a PR?