I am trying to write a test that covers the behavior more or less of the entire application - technically these are more integrative tests than strictly unit tests - and I feel as though there is something missing in my understanding of how processes are started and named that is stopping me from being able to test the application the way I want.
How do I instruct the application code of the names assigned to the associated processes it calls on during the run of a test? I have seen similar questions relating to this issue/error but I am too inexperienced to see the answer for my particular context.
I have the following Supervisor, which starts 3 custom written workers and another from this library
defmodule Krown.Supervisor do
use Supervisor
# code omitted for brevity
def start_link(init_arg \\ []) do
Supervisor.start_link(__MODULE__, init_arg, name: __MODULE__)
end
def init(args \\ []) do
children = [
{FileSystem.Worker,
[
name: Keyword.get(args, :file_event_name, FileEventNotify),
recursive: true,
dirs: [Application.fetch_env!(:my_app, :event_dir)]
]},
{Dispatcher, [name: Keyword.get(args, :dispatcher_name, Dispatcher)]},
{Converter, [name: Keyword.get(args, :converter_name, Converter)]},
{Uploader, [name: Keyword.get(args, :uploader_name, Uploader)]},
]
Supervisor.init(children, strategy: :one_for_one)
end
end
The Dispatcher, which monitors messages in FileSystem.Worker
, looks like this:
defmodule Krown.Dispatcher do
use GenServer
# code omitted for brevity
def init(_) do
FileEventNotify
|> Process.whereis()
|> FileSystem.subscribe()
{:ok, nil}
end
def handle_info({:file_event, _watcher_pid, {fpath, events}}, _state) do
Path.dirname(fpath)
|> Path.split()
|> List.last()
|> case do
"raw" ->
Converter
"wav" ->
Uploader
end
|> case Process.whereis() do
nil ->
Logger.warn("Unable to locate pid with name: #{pid_name}")
raise "#{pid_name} PID NOT FOUND"
pid ->
GenServer.cast(pid, {:perform, fpath})
end
{:noreply, nil}
end
And a basic test for illustrative purposes:
test "something contrived "
spec = %{
id: __MODULE__,
start: {
DescribedModule,
:start_link,
[[uploader_name: Test.Uploader,
converter_name: Test.Converter,
file_event_name: Test.FileEventNotify,
event_dispatcher_name: Test.Dispatcher]]
}
}
pid = start_supervised!(spec, restart: :temporary)
# create a file that will trigger FileEventNotify and Dispatcher
File.touch("some_fpath")
assert Process.alive?(pid)
# Uploader will remove the file once it has completed #perform
refute File.exists?("some_fpath")
end
Declaring names for each GenServer in the test avoids the ** (EXIT) already started: #PID<0.284.0>
type error, but it effectively breaks my application in the Dispatcher, since the dispatcher relies on there being static names for the two processes it sends messages to in #perform/2
.
So I think to myself, “maybe I am missing something, possibly the use of a Registry?”. But I played with that and it took me back to my first problem whereby the application code has no way of knowing what the dynamically named Registry is.
I noticed that if I comment out mod: {Krown, []}
from mix.exs, then I could run the tests successfully - but obviously this is not something I can use successfully in the real world, so what’s the point?
Maybe I am thinking about this all wrong. I feel I am stuck in my understanding. I guess I am writing more integration tests vs unit tests? If so, should I structure them differently?
I also have doubts the vocabulary I am using to express the issue I am having, so any feedback on that is also welcome.