[erlang-questions] Mocks and explicit contracts

Ola Bäckström Ola.Backstrom@REDACTED
Thu Nov 19 16:12:30 CET 2015


I don't see that how one can get away with running independent tests in parallel with mocks or doing the get_env-thing.

The latter do work, though, IFF all concerned tests are just expecting the same thing from the called function... but most likely you have some tests that would check different scenarios. One test might check that the System Under Test behaves well if twitter does a positive response while another might check that the SUT  does  not fail in a catastrophic way if the twitter call gives garbage as response.

From a performance and code readability perspective I'd rather have normal module calls like:
       some_twitter_api:get_username(Username)

(and having to use the excellent meck mock library) instead of something like
      ?TWITTER_API:get_username(Username)
Or
      (twitter_api()):get_username(Username)

In particular, having every module-prefixed call doing an application:get_env-call underneath does not feel very erlangish.
And it doesn't really solve the running-the-test-suite-concurrently issue, in the more general case where tests are expecting different return values from a function.

Regarding mocks (meck) being slow, it is the meck:new that costs time. That function swap the module with a runtime generated module. (meck:unload is similar)
If you, for instance, make a wrapper around meck that calls meck:new and meck:unload once in the test suite and uses meck:delete to remove mocks between individual test cases you can increase test suite execution speed a lot.

/Ola

-----Original Message-----
From: erlang-questions-bounces@REDACTED [mailto:erlang-questions-bounces@REDACTED] On Behalf Of Roger Lipscombe
Sent: den 19 november 2015 13:36
To: erlang-questions@REDACTED
Subject: [erlang-questions] Mocks and explicit contracts

In http://blog.plataformatec.com.br/2015/10/mocks-and-explicit-contracts/,
José Valim suggests a way to use dependency injection to more easily unit test Elixir code.

However, it doesn't translate to Erlang...

In one example, he stores the dependency (as a module) in the application environment and then defines a private function to retrieve it:

    defp twitter_api do
        Application.get_env(:my_app, :twitter_api)
    end

Then it can be called as:

    twitter_api.get_username(username)

This doesn't translate to Erlang (or, rather, it's unwieldy), because we can't do the following:

    twitter_api() ->
        application:get_env(my_app, twitter_api, default_twitter_api).

    show(Username) ->
        % syntax error before: ':'
        twitter_api():get_username(Username)
        etc.

We need more brackets:

        (twitter_api()):get_username(Username)

This gets a bit ugly, though less so with macros:

    -define(TWITTER_API,
        % note the extra brackets:
        (application:get_env(my_app, twitter_api, default_twitter_api))).

    show(Username) ->
        ?TWITTER_API:get_username(Username).

He goes on to show another form of DI which takes advantage of the fact that Elixir allows inline modules, Again, we can't do this in Erlang.

So:
- Is Jose's approach applicable to Erlang at all?
- Are there idiomatic ways to do something like this in Erlang?

I know about (and use) 'meck', which is awesome, but has the disadvantage that it mocks (verb) globally, which limits running tests in parallel. It can also be quite slow, because it compiles the mocks on the fly. José's approach uses mocks (noun) locally.
_______________________________________________
erlang-questions mailing list
erlang-questions@REDACTED
http://erlang.org/mailman/listinfo/erlang-questions


More information about the erlang-questions mailing list