Accessing Private Functions in Elixir

While reading Meck's code I noticed something very interesting!

Meck is a testing library written in Erlang that recompiles modules replacing the original module with a testing module.

:meck.expect(MyModule, :my_function, fn x -> x + 1 end)

The above code will change the original function called my_function from the module MyModule to behave like the anonymous function that was passed.

This works just fine, but there's an issue with this approach. Calculating test coverage of a module is much more complicated now that the module can change during the test execution.

To work around this problem Meck applies a very interesting technique:

  • It stores the existing coverage data that is generated by the cover application that comes with Erlang;
  • Replaces the original module with the new implementation;
  • Once "unloaded" with meck.unload(MyModule) it then restores the coverage data for that module;

The problem is that cover doesn't expose ways to change the coverage data in a simple way like described above.

Meck's solution is to change cover's code during execution to export some specific private functions: meck_cover.erl

Once Meck can access these privates functions then it's easy to reconstruct the cover data that needs to be generated at the end!

If we wanted to be more aggressive and expose all private functions from cover, this code should do the job:

{_, binary, _} = :code.get_object_code(:cover)
{:ok, {_, [{_, {_, abstract_code}}]}} = :beam_lib.chunks(binary, [:abstract_code])
{:ok, module, binary} = :compile.forms(abstract_code, [:export_all])
:code.load_binary(module, '', binary)

That's it! The fact that the Erlang VM allows a module to be changed while executing opens up this kind of clever solution!

Posted on August 3, 2019