How do I do dependency injection and mocks in erlang?

dependency-injection, erlang, mocking, unit-testing

Solution

There are two things to consider here...

You need to separate out all your code into 2 different types of modules:

- pure functional modules (aka side-effect free modules)

- modules with side-effects

(You should read up on that and be sure that you understand the difference - the most typical side-effect - and the one that is in your sample code - is writing to the database).

The modules that are pure functional become trivial to test. Each exported function (by definition) always returns the same values when the same values are put in. You can use the EUnit/Assert framework that Richard Carlsson and Mickael Remond wrote. Bish-bash-bosh, job's a good 'un...

The key thing is that about 90% of your code should be in pure functional modules - you dramatically shrink your problem. (You might think this is not 'solving' your problem, merely 'reducing' it - and you would be mostly right...)

Once you have achieved this separation the best way to unit test the modules with side-effects is to use the standard test framework.

The way we do this is not to use mock-objects - but to load the database in the init_per_suite or init_per_test functions and then run the modules themselves...

The best way is to move straight over to system tests as soon as possible for this though as the unit tests are a pain to maintain - so enough unit tests to get you to a system-test round-trip and no more (even better delete the db unit tests as soon as possible).

Problem

When writing code in Java, it is very helpful to embrace composition and dependency injection to make it possible and easy to do pure unit testing by mocking collaborating objects. I find that doing the same in Erlang is less straightforward and makes for dirtier code. That's likely to be my fault, as I'm quite new to Erlang and quite addicted to JUnit, EasyMock and java interfaces... Let's say I have this stupid function: ``` %% module mymod handle_announce(Announce) -> AnnounceDetails = details_db:fetch_details(Announce), AnnounceStats = stats_db:fetch_stats(Announce), {AnnounceDetails, AnnounceStats}. ``` When unit testing `mymod`, I only want to prove that `details_db` and `stats_db` are invoked with the right parameters, and that the return values are used correctly. The ability of `details_db` and `stats_db` to generate correct value is tested in other places. To solve the problem I could refactor my code this way: ``` %% module mymod handle_announce(Announce, [DetailsDb, StatsDb]) -> AnnounceDetails = DetailsDb:fetch_details(Announce), AnnounceStats = StatsDb:fetch_stats(Announce), {AnnounceDetails, AnnounceStats}. ``` And test it this way (basically stubbing the calls directly into the test module): ``` %% module mymod_test handle_announce_test() -> R = mymod:handle_announce({announce, a_value}, [?MODULE, ?MODULE, ?MODULE]), ?assertEqual({details,stats}, R). fetch_details({announce, a_value}) -> details. fetch_stats({announce, a_value}) -> stats. ``` It works, but the application code becomes dirty and I always have to carry around that ugly list of modules. I've tried a couple of mock libraries (erlymock and (this other one) but I wasn't satisfied. How do you unit test your erlang code? Thanks!

Original source