Is there a way to test private functions in modules in ExUnit of Elixir?

It's possible to change the visibility of a function depending on the environment using a macro:

defmacro defp_testable(head, body \\ nil) do
  if Mix.env == :test do
    quote do
      def unquote(head) do
        unquote(body[:do])
      end
    end
  else
    quote do
      defp unquote(head) do
        unquote(body[:do])
      end
    end
  end
end

Then you can expose functions to your tests as follows:

defp_testable myfunc do
  ...
end

I recommend using this sparingly for the reasons given in José's answer. It's not a substitute for testing the external behavior of a module. It can be valuable in certain scenarios, though.

(Source)


In your module definition, you can a @compile directive to export your private functions only in the test environment.

defmodule Foo do
  @compile if Mix.env == :test, do: :export_all

  # This will be exported for tests
  defp bar() do
  ... code ...
  end
end

Private functions cannot be called from outside their modules, even in the context of testing with ExUnit.

Others suggested complex ways to expose private functions for testing, hence I suggest these other two which seem much simpler:

1) make the function you want to test public, but mark it with @doc false, thus meaning that it is not part of the public API of your module.

2) alternatively, if the function you want to test is defp foo then create a def test_foo, in the same module, which takes the arguments you want to vary during your tests, adapts them to what foo expects, and finally calls foo. You can also generate your special test function only during testing like this

  if Mix.env == :test do
    def test_foo ...
  end

Any of these alternatives is simpler and requires no extra dependencies in your project.

Of course, as others mentioned, the general rule is to avoid testing private functions, but sometimes testing them is the way to ease testing and raise the coverage of your tests.

Good coding!


No, there is no way to test them via ExUnit.

I personally avoid testing private functions because usually you end up testing implementation instead of behaviour and those tests fail as soon as you need to change the code. Instead, I test the expected behaviour via the public functions, breaking them in small, consistent chunks.

Tags:

Elixir