Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

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

Tags:

elixir

Functions defined by defp aren't exported so I can't execute them in places other than in the module.

like image 977
Noah Blues Avatar asked Jan 06 '14 11:01

Noah Blues


People also ask

What is doctest in Elixir?

Doctests allow us to generate tests from code examples found in @moduledoc and @doc attributes. To do this, invoke the doctest/1 macro from within your test case and ensure your code examples are written according to the syntax and guidelines below.


4 Answers

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.

like image 144
José Valim Avatar answered Oct 22 '22 22:10

José Valim


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
like image 27
Michael Bishop Avatar answered Oct 22 '22 23:10

Michael Bishop


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)

like image 9
acj Avatar answered Oct 22 '22 21:10

acj


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!

like image 3
mljrg Avatar answered Oct 22 '22 23:10

mljrg