Completely replace a dependency with a mock


I have an app called :my_app that has a dependency called :my_dep. :my_dep connects to the internet and downloads a bunch of files. Every time I run :my_app, it executes :my_dep because :my_dep is a runtime dependency. So, when I run tests, launch the app in my DEV environment or simply run it, :my_dep always connects to the internet to download files.

If :my_dep cannot connect, it fails, and because it is a runtime dependency, :my_app fails to launch.

My Approach

The current system means I can’t run local tests nor do anything without a connection to the internet. So my attempt at fixing this was to follow the approach suggested by mocks and explicit contracts and to create an interface (add a boundary between :my_app and :my_dep) and to create a mock that implements the interface.

Following is an example of how I am implementing this technique:

Geolocation Module of MyApp:

defmodule MyApp.Geolocation do

  @geo Application.get_env(:my_app, :my_dep_api)

  require Logger

  @spec geolocate(String.t) :: String.t
  def geolocate(ip) do
    |> @geo.find()
    |> get_country(ip)

  @spec get_country({:ok, [String.t]}, String.t) :: String.t
  defp get_country({:ok, [country | _tail]}, _ip), do: String.upcase(country)

DEV config file of MyApp:

config :my_app,
  my_dep_api: MyDep.StaticMock

The StaticMock file:

defmodule MyDep.StaticMock do
  @behaviour MyApp.IMyDep

  @behaviour MyApp.IMyDep
  @spec find(String.t) :: {:ok, [String.t]}
  def find(_ip), do: {:ok, ["ES", "cat", "b"]}


The issue here, is that when I launch my app in DEV, it still runs the original my_dep app and it still tries to connect and download the files.


How can I fix this?

Application.get_env(:my_app, :my_dep_api) vs
config :bidtor, my_dep_api: MyDep.StaticMock

Is this just a copy/paste error?

Yes. Fixed ! (thanks)

Have you tried forcing recompilation? IIRC the module attribute is read from the application env at compile-time and stored in the BEAM file.

I have deleted the _build and deps folders and forced recompilation. The issue still persists.

Apologies for the confusion, I totally skipped this part of your question:

The right place to fix this isn’t in MyApp.Geolocation, but rather where :my_dep is being started: might be in :my_app's application.exs or in mix.exs.