Search code examples
testingelixirdelegationex-unit

Test method delegation in Elixir


I've got two modules:

defmodule Base.ModuleOne do
  def do_stuff(opts) do
    "some stuff with opts"
  end
end

defmodule Base.ModuleTwo do
  defdelegate do_stuff(opts), to: Base.ModuleOne
end

What is the proper way of testing delegation without copy-pasting tests for Base.ModuleOne.do_stuff/1 function under different namespace?


Solution

  • As of the writing of this answer, I haven't found an "official" way to test defdelegate either. The only choices I can think of in your case are:

    1. Just ignore testing of Base.ModuleTwo.do_stuff(opts), assuming you have your bases covered in tests for Base.ModuleOne.do_stuff(opts).
    2. Move the bulk of (if not all of) the tests in Base.ModuleOne.do_stuff(opts) up to the tests for Base.ModuleTwo.do_stuff(opts) so your tests become directly concerned with the public interface of your module, relegating Base.ModuleOne to be "private" implementation detail.
    3. Since mocking would seem to be generally out of favour in Elixir, consider replacing defdelegate with dependency injection (using a function or module; see previous link for details), which would then allow you to write tests in the following kind of way:

    Module:

    defmodule Base.ModuleTwo do
      def do_stuff(opts, dependency \\ Base.ModuleOne)
        dependency.do_stuff(opts)
      end
    end
    

    Test:

    defmodule ModuleTwoTest do
      use ExUnit.Case
    
      describe "Base.ModuleTwo.do_stuff/2" do
        defmodule TestDependency do
          def do_stuff(_opts) do
            send self(), :do_stuff
          end
        end
    
        test "calls do_stuff/1 on its dependency" do
          opts = %{foo: "bar"} # or whatever the `opts` should be
          Base.ModuleTwo.do_stuff(opts, TestDependency)
          assert_received :do_stuff
        end
      end
    end