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.