What API mocking tool are Elixir teams using?

We’re evaluating API mocking tools for OpenAPI-based projects and would love to hear what other teams are using.

We’re particularly interested in:

  • OpenAPI compatibility
  • Mock server capabilities
  • Easy collaboration
  • Integration with testing workflows

What has worked best for your team?

Hello,

What are you trying to mock in elixir? External APIs or your own API?

2 Likes

Bypass - for HTTP
Mimic - for in process APIs, things that [may] call modules which HTTP

1 Like

I have been using Sham over Bypass for a while now.

Mimic for quick mocking

Mox occasionally.

When testing with something that uses Req, I just use the built in Req.Test things. Super handy.

2 Likes

Efx moves mocking in new direction: Efx - A library to declaratively write testable effects | Elixir Forum
I used in one of my pet projects

2 Likes

What were your impressions?

Hi @dimitarvp, biggest win compared to Mox (up to Efx the best option) is no hassle with configurations and precondition that boundary needs to have behaviour. In testing, you bind your boundary functions and you are good to go.

is no hassle with configurations

Mimic works in the same way too

Unique power of Efx is

Efx uses the fairly new ancestor-keys from the process dictionary to track down the bindings for tests event for child-processes and therefore, more tests can be implemented asynchronously.

1 Like

I was about to say but got beaten to it: how is this different than Mimic?

Though I have to say: explicit or implicit marking of effects is very, VERY, welcome. Don’t get me wrong.

I glanced over Efx and yeah, just as I expected, it’s another way to NOT do proper dependency injection. Why are we so afraid to code against protocols and pass around structs which implement them?

2 Likes

Why are we so afraid to code against protocols

protocols are about polymorphism based on the struct type, whereas we’re discussing behavior that depends on the environment. For example, in test env we may want to avoid making real HTTP calls or spawning real processes.

Mox is a great fit when you’re abstracting an external dependency behind a behavior. But for testing internal implementations, I find e.g. Mimic much more convenient. It lets you mock internal modules without adding extra behaviors, configuration, or maintenance overhead :sweat_smile:

2 Likes

HTTP call or spawning a process is already a dependency. You fall prey of the chicken-egg problem. It not that mox enforces you to introduce behaviours where they don’t belong. On the contrary, it highlights the places where the dependency injection must have happened.

We migrated to Req when it appeared in 10 minutes, because—guess what—our HTTP calls were already abstracted.

That is exactly what Mox intentionally prevents. Not because they could not figure it out. Because it improves the code quality, while adaptive, indiscriminate, and promiscuous mocking library welcomes God spaghetti objects in your code.

Exactly. The best feature of mox is not it helps to test stuff, but rather it literally points out to the places in the code that were forgotten to be decoupled with dependency injections.

Libraries being so friendly to swallow whatever people want to mock are no better than just marking tests as @tag skip: true on that matter.

1 Like

The mantra that protocols are about data really doesn’t make sense to me. Implementers of a protocol can also depend on the environment… Maybe I should write a blog post about it. Seems like there are some deficiency in the community regarding dependency injection.

3 Likes

If you mention “dependency injection is a boundary allowing to easier decouple the code” there, I would be super-grateful because I have it (writing such a text) on my to-do list for months already.

That’s interesting - so in other words do u think I should abstract away e.g. GenServer? :thinking:

e.g.

# moduleA
def foo_a(...) do
   ModuleB.fun_b(...)
   |> actiona
   |> actionb
   |> ...
end

#moduleb 
use GenServer
def fun_b(....) do
     GenServer.call(...)
end

def init(..) ...

In the example above I usually stub return data from moduleB.fun_b to write unit tests fro moduleA
Mimic allows me to do it without configuration burden, but I see that using mock implementation for GenServer while testing may work too - GenServer has already behavior - it is enough to mock it’s functions.
The only problem I see it to not easy distinguish of GenServer calls - as we would have to differ them based on pattern matching args which might be not very readable

I mean instead

stub(ModuleB, foo, fn -> returnA end)
stub(ModuleC, foo, fn -> return end)

we would need

stub(GenServer, call, fn _, 
                           {:update, "id"} -> returnA # moduleB call
                           {:update, "item"} -> returnB  # moduleC call
                           
end)

another problem is deep call sequence - we would have to stub all GenServer call/cast down in call stack - where with Mimic we can isolate it right away in the testing module without worring about dependencies very deep down in the code :thinking:
please correct me if I missed sth :folded_hands:

Please do consider to update Elixir doc then :sweat_smile:

Protocols are a mechanism to achieve polymorphism in Elixir where you want the behavior to vary depending on the data type.

ok, so do u suggest to pass env as a first arg to protocol to get different behavior, am I right?
Why not use behaviors instead? :thinking:

Please do! I would love to read it :folded_hands:

You can’t pass something different than the implementer as first argument when implementing a protocol.

Also, why would you stub a GenServer? Just spin one in your test and pass the pid to your function or whatever. This tells me there might be bigger, more general, problems in the code base.

Are we using OTP to write procedural code, with god objects and all?

1 Like

I know :smiley: The question was - why I should use protocol and passing envs to it - instead of behavior and use Mox?

It’s not a good idea for unit tests. Spawning a process often involves database calls or dependencies on other processes, which can force tests to use async: false and require a much more complex setup. That’s unnecessary overhead when you’re writing unit tests :grinning_face_with_smiling_eyes:

You do the database calls in the test using the Ecto sandbox and provide the data to the spawned process.

1 Like

I am not sure how you came from stubbing moduleB to mocking the GenServer. Let’s make a step back and discuss the business process. You have a surfacing GenServer that relies on the implementation of some functionality hidden behind the moduleB façade.

The proper design is the GenServer is not tied to moduleB but rather implements a process layer above it. That evidently means moduleB has to be a dependency. Just do @foo_handler Application.compile_env(:foo_app, :foo_handler, ModuleB) if you want a static linking, or pass foo_handler: ModuleB to foo_a if you want it dynamic.

2 Likes