Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How do I do dependency injection and mocks in erlang?

Tags:

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!

like image 516
Matteo Caprari Avatar asked Jul 29 '09 19:07

Matteo Caprari


People also ask

Is mocking a dependency injection?

Dependency injection is a way to scale the mocking approach. If a lot of use cases are relying on the interaction you'd like to mock, then it makes sense to invest in dependency injection. Systems that lend themselves easily to dependency injection: An authentication/authorization service.

What is mock dependency?

Mocking is a way to replace a dependency in a unit under test with a stand-in for that dependency. The stand-in allows the unit under test to be tested without invoking the real dependency.


2 Answers

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).

like image 198
Gordon Guthrie Avatar answered Oct 16 '22 17:10

Gordon Guthrie


I second what Guthrie says. You'll be surprised by how much of your logic can be pulled out into pure functions.

One of the things I've been tying lately with the new parameterized modules is to use paramterized modules for the dependency injection. It avoids the problem with parameter lists and process dictionaries. If you can use the recent versions of erlang that might be a good fit as well.

like image 23
Jeremy Wall Avatar answered Oct 16 '22 16:10

Jeremy Wall