Testing a behaviour implementation that uses super

I’m trying to write a test for a module that does something like this:

defmodule Implementation do
  use MyBehaviour # imports default implementations

  @impl MyBehaviour
  def foo(args) do
    # ... do some stuff ... 
    super(args)
  end
end

I’m using Mox and have set this module up to use mock dependencies in the test environment, but in this case with super, I’m struggling to think of a way to test the ...do some stuff... code in isolation, without the overridden function being executed.

I’m not sure there even is a way for testing this without the call to the overridden function. Personally I’d try to avoid super as much as possible. Mox won’t help you here, because it’s about mocking an implemenation. You’re trying to test an implementation.

4 Likes

As @LostKobrakai notes this isn’t about mocking, this is about testing the implementation. If you want to test do some stuff in isolation, extract it to a function in the Implementation module and call it directly.

Thanks for the replies.

I should have been clearer about Mox. I’m using Mox because this module also uses other behaviours - so Mox is providing Mocks for those, allowing me to test the implementation of this module.

Splitting the logic into a separate function would work - but in that case I would want to make the function private. It seems like an anti-pattern to make it public just for testing. And I’ll still be left with an untested public function. :expressionless:

To provide a specific example, I’ve overridden HTTPoison.post/3 to automatically encode the request into JSON:

def post(url, body, headers \\ []) do
  super(url, Jason.encode!(body), [{"Content-Type", "application/json"} | headers])
end

I’m wondering if using super was the best choice. Maybe what I should really do is something like @httpoison.post(...), what would allow me to swap @httpoison with a mock implementation in my tests, depending on configuration.

@adamu It happens from time to time that you encounter a function which you don’t want to expose as a public function, but which for some other reason has to be. Elixir’s standard library has a couple of these, and some libraries have them as well. There is a common and semi-standard way to deal with this: Add @doc false above the function to hide it.

Yeah it sort of seems like you’re mixing up the boundary between behaviour and implementation here. If super is something that is supposed to be mockable then use and overriding the function seems like the wrong approach.