Fail tests if external (HTTP) apis are called

I was wondering if there is an easy automated way to do this. We have a test suite that we run and we mock out external API calls for obvious reasons. But sometimes something does not get mocked like it should. It would be wonderful if there was a way to automatically catch that across our test suite.

Is there a solution for this? My google-fu failed me

Thanks! :smiley:

I don’t know of any way to do this automatically through code but could you just turn your networking off and see what fails?

I usually use fake API info to configure the test env so if a request goes out because the API module wasn’t mocked it fails obviously.

Does anyone have other thoughts on this? It feels like something that might be possible, or perhaps at least I wish it was possible.

I wonder if you could at least detect this via tracing, e.g. tracing low level functions like :ssl.connect/3,4.

One way to do it is to add a behaviour for the HTTP calls (get, post, delete, etc) and then use the Mox implementation to handle all HTTP responses in the test environment:

defomodule MyApp.HttpClient do

  def get(url, headers, options) do
    impl().get(url, headers, options) 
  end

  defp impl do
    Application.get_env(:my_app, :http_client_impl, MyApp.HttpClient.DefaultImpl) 
  end
end
# config/test.exs

config :my_app, :http_client_impl, MyApp.HttpClient.MockImpl

As an extra layer of fault-tolerance, perhaps you could check in the actual client itself to make sure nobody calls it manually in the wrong environment:

defmodule MyApp.HttpClient.DefaultImpl do
  def get(url, headers, options) do
    if Application.fetch_env!(:my_app, :http_client_impl) != __MODULE__,
      do: raise("application is not configured to use this client")

    # ...
  end
end

While I do often reach for Mox for testing. What I’m looking for here is a way to detect HTTP calls without having to inject a behavior because I’m looking to catch HTTP calls that are inserted by accident, including any calls added in libraries.

I see, that’s an interesting use case. I can only think of 3 solutions then:

  1. Does the library support overriding the base URL or domain used on the HTTP calls? Then you can change it to localhost and use the Bypass library to mock the responses.
  2. Otherwise, you would need to open a PR on the library repository (or clone it) to allow overriding the HTTP client (by adding a behaviour) or base URL (solution 1).
  3. Add a behaviour/wrapper and call it instead of the library