Lasse Ebert
Jul 28, 2017 · 4 min read

Hi Nimrod

Thanks for reading my blog post and considering me eligible to give an opinion on your library :)

Also, sorry for the late reply. I just came home from a summer trip.

I can clearly see a lot of use cases in which ElixirMock is useful, and it seems to have a nice DSL that makes it comfortable to create on-the-fly mocks.

However, I have a problem with injecting behaviour on runtime in this way. This might just be a matter of taste, but I will try to explain what drawbacks I think it has.

Loss of compile-time optimizations and static analysis

This is a technical drawback. When any module can be parsed in to a function on runtime, the compiler can not make any assumptions and optimization. Compiler warnings might also be lost, e.g. if the code is calling a function that does not exist in the module.

I do not know enough about how Elixir and Erlang compiles to know the real drawbacks of this, if any, but I feel something is lost in the code on this point.

Loss of certainty for the reader of the code

I come from Ruby. Ruby uses dependency injection a lot and it’s often hard to find out which piece of code is called in a method call. Consider this code for example:

def first_element(array)
array.first
end

This code seems simple, but when debugging a problem it can be very complex to find out where the first method actually is defined.

First, what is array? array can be any object that answers to the first method. It could be an Array, a Hash, any other module implementing Enumerable or any other object that happens to have a first method.

Next, if we by chance know that array is in fact an Array, the first method could be monkey-patched to mean something differently than returning the first element of the array.

My point is that just by looking at the ruby code, the reader can not know anything about what is going on.

Now consider this Elixir code:

def first_element(list, list_module \\ List) do
list_module.first(list)
end

This is kind of the same issue as with the ruby code. The reader can not tell what this code is doing just by looking at the code.

Not useful when the dependency is nested

If we need to create a mock for an external API client to not make real HTTP requests in out test code, the approach with injection becomes difficult when the API client dependency is nested.

For unit tests this can be dealt with by always injecting all dependencies, which would mean that nested dependencies would not exist in test.

But for integration testing where we test the whole dependency chain and still need to swap an external API client call with a mock, this becomes hard.

Example: We want to integration test our REST API endpoint that looks like this:

defmodule UserController do
def show(conn, params) do
user = UserFetcher.fetch_with_twitter(params["id"])
render_user(conn, user)
end
end
defmodule UserFetcher do
def fetch_with_twitter(id, twitter_api \\ TwitterAPI) do
user = load_user_from_db(id)
twitter_user = twitter_api.get_user(user.twitter_name)
merge_details(user, twitter_user)
end
end

We made sure we can swap the TwitterAPI module in UserFetcher, but this does not matter when we integration test on the GET /users/:id endpoint. For this usecase we need to swap it globally on compile-time.

Benefits

There are clearly some benefits too, when using dependency injection instead of choosing modules at compile-time.

  • It is simpler to adapt the mock for a very specific use-case.
  • The mock is defined where it is used, which improves readability of the test code.
  • Boilerplate code for verifying calls and such is handled by your library. I find that this is always very individual with compile-time mocks, which means more code to write a mock.

Status on my mocks

It’s been a little while since I write the blog post, and at the time I was still new in the mocking business in Elixir.

I still use the pure-Elixir solution. I admit that some of my mocks are somewhat advanced, since I often need to set different responses given different inputs and also verify which calls are made. I have not yet run into a situation when this approach became too difficult.

Don’t give up :)

This is just my opinion. I’m sure there are other opinions on how the create mocks when testing in Elixir. There might also be cases within the same project when the compile-time mock and the runtime-mock could both be used in different use-cases.

This became a longer answer than I thought. I really hope I did not demotivate you to work on ElixirMock. The Elixir community is build by passionate people like you and there is plenty of room for different opinions on how to solve a specific problem.

/ Lasse

    Lasse Ebert

    Written by

    Developer, Elixir

    Welcome to a place where words matter. On Medium, smart voices and original ideas take center stage - with no ads in sight. Watch
    Follow all the topics you care about, and we’ll deliver the best stories for you to your homepage and inbox. Explore
    Get unlimited access to the best stories on Medium — and support writers while you’re at it. Just $5/month. Upgrade