micah
Mox with Applications - to start or not to start
Hello all! I’m in the process of writing an application with a couple moving parts, but unit testing it is making me question everything.
My application supervises three processes:
children = [
{DynamicSupervisor, strategy: :one_for_one, name: MyApp.SuperSpider},
{MyApp.Database, name: MyApp.CurrentDatabase},
{MyApp.SiteAPI, name: MyApp.SiteAPI},
]
When the SiteAPI starts, it loads and starts processes based on what is in the database:
def init(opts) do
super_spider = opts |> Keyword.get(:super_spider, MyApp.SuperSpider)
db = opts |> Keyword.get(:db, MyApp.CurrentDatabase)
sites = db |> Database.get_sites()
spiders = sites |> Enum.reduce(%{}, fn (site, acc) ->
{:ok, new_pid} = DynamicSupervisor.start_child(
super_spider, {MyApp.Spider, {site, db}}
)
Map.put(acc, site.id, new_pid)
end)
{:ok, %{db: db, super_spider: super_spider, spiders: spiders}}
end
Everything as near as I can tell works, except that when I start the unit tests using mix test, the application first uses the real “database” instead of the MockDatabase which is defined in test/test_helper.ex:
Code.require_file("test/mock_dets.ex") # Mocking dets with ets
Mox.defmock(MyApp.MockRequest, for: MyApp.Request)
Mox.defmock(MyApp.MockDatabase, for: MyApp.Database)
Application.put_env(:my_app, :use_delay, false)
Application.put_env(:my_app, :request, MyApp.MockRequest)
Application.put_env(:my_app, :database, MyApp.MockDatabase)
Application.put_env(:my_app, :dets, MyApp.MockDETS)
ExUnit.start()
When I run the unit tests, the Application is started before the Mox(s) are configured, and the real Database is used. What can I do to avoid this?
I’ve tried not starting the application with “–no-start” but this also stops the dependencies (like Mox) from starting.
Most Liked
micah
@stefanchrobot Thank you for your quick response! MyApp.Database is both a behavior and a stub that routes it through the application environment variable “:database”
defmodule MyApp.Database do
@doc """
Create a new database process
"""
@callback start_link() :: GenServer.on_start()
def start_link(), do: impl().start_link()
@callback start_link(_ :: integer) :: GenServer.on_start()
def start_link(arg), do: impl().start_link(arg)
@doc """
Information for adding to supervision trees
"""
@callback child_spec(term()) :: Supervisor.child_spec()
def child_spec(arg), do: impl().child_spec(arg)
# Lots more functions following a similar pattern here...
defp impl, do: Application.get_env(:my_app, :database, MyApp.FileDB)
end
The whole app is open source, but it’s really messy (I’ve made some poor design/naming decisions) and definitely a work in progress: ~electric/shop_local: apps/product_search/lib/database.ex - sourcehut git
ajur58
I had a very similar use-case to yours and almost gave up. Then I found your implementation linked above and it works!! Thanks for making it open source!








