I have an umbrella app.
Some of the apps inside depend on other apps in the umbrella, unsurprisingly.
I’m writing a test for one of the apps which currently relies on starting a process using one of the other apps.
Almost all such processes across these apps use an app-specific or purpose-specific Registry.
This is the error I get:
23:04:13.529 [error] GenServer {Registry.Mobs, 1} terminating
** (stop) exited in: GenServer.call({:via, Registry, {World.LocationRegistry, "2"}}, {:mobs, #Function<1.42779424/1 in Mobs.Bird.handle_cast/2>}, 5000)
** (EXIT) no process: the process is not alive or there's no process currently associated with the given name, possibly because its application isn't started
(elixir) lib/gen_server.ex:729: GenServer.call/3
(mobs) lib/mobs/bird.ex:79: Mobs.Bird.handle_cast/2
(stdlib) gen_server.erl:601: :gen_server.try_dispatch/4
(stdlib) gen_server.erl:667: :gen_server.handle_msg/5
(stdlib) proc_lib.erl:247: :proc_lib.init_p_do_apply/3
Last message: {:"$gen_cast", :try_to_mate}
State: %Mobs.Bird{controller: #PID<0.395.0>, gender: :female, id: 1, lifespan: 39, location_id: "2", name: "a bird", pregnant: nil}
My test_helper.exs
is trying to provide support to the mix.exs
file which requires the apps by doing this:
Application.ensure_all_started(:world)
Application.ensure_all_started(:mobs)
Application.ensure_all_started(:controllers)
Application.ensure_all_started(:life)
ExUnit.start()
The World.Application
module looks like this:
defmodule World.Application do
# See http://elixir-lang.org/docs/stable/elixir/Application.html
# for more information on OTP Applications
@moduledoc false
use Application
def start(_type, _args) do
import Supervisor.Spec, warn: false
# Define workers and child supervisors to be supervised
children = [
supervisor(Registry, [:unique, World.LocationRegistry], id: :location_registry),
supervisor(Registry, [:unique, World.PathwayRegistry], id: :pathway_registry),
supervisor(World, [%{spawn_locations: Application.get_env(:world, :spawn_locations)}])
]
# See http://elixir-lang.org/docs/stable/elixir/Supervisor.html
# for other strategies and supported options
opts = [strategy: :one_for_one, name: World.Supervisor]
Supervisor.start_link(children, opts)
end
end
This is the test as it stands:
defmodule Mobs.BirdTest do
use ExUnit.Case
doctest Mobs.Bird
test "only mates with birds" do
import IEx
{:ok, foo} = World.Location.start_link(
%World.Location{
id: "2",
name: "center of the universe",
description: "what's on the tin",
pathways: []}
)
female_bird = Mobs.Spawn.birth(
%{module: Mobs.Bird, location_id: "2", gender: :female})
male_dwarf = Mobs.Spawn.birth(
%{module: Mobs.Dwarf, location_id: "2", gender: :male})
:ok = Mobs.Bird.try_to_mate(female_bird)
end
end
The obviously relevant code from Mobs.Bird
is :
defmodule Mobs.Bird do
def try_to_mate(id) do
GenServer.cast(via_mob(id), :try_to_mate)
end
def handle_cast(:try_to_mate, state) do
looking_for = case state.gender do
:male -> :female
:female -> :male
end
possible_partners =
World.Location.mobs(
state.location_id,
fn({{module, id}, info}) -> module == __MODULE__ && info.gender == looking_for end)
if Enum.any? possible_partners do
partner = Enum.random possible_partners
case [state.gender, elem(partner, 1).gender] do
[:male, :female] -> __MODULE__.pregnantize(elem(partner, 0))
[:female, :male] -> __MODULE__.pregnantize(state.id)
end
end
{:noreply, state}
end
end
Similarly, the obviously relevant code from World.Location
is :
defmodule World.Location do
def mobs(loc_id)do
mobs(loc_id, fn(_x) -> true end)
end
def mobs(loc_id, filter) do
GenServer.call(via_tuple(loc_id), {:mobs, filter})
end
def handle_call({:mobs, filter}, _from, state) do
mobs = Enum.filter(state.entities, filter)
{:reply, mobs, state}
end
end