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:
cover
application that comes with Erlang;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!