fredmcgroarty
"(EXIT) already started:" when testing parts of application
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.
First Post!
eksperimental
I’m not 100% this is what you may need, but you can pattern match on the context of the test.
test "something contrived ", %{test: test_name} = _context do
# test_name is a unique string that you can use to identify your GenServers.
end
you can also use setup/2 to create a genserver automatically, and pass it through the context, but in this case you will have to randomly generate the name as the test name won’t be available.
Popular in Questions
Other popular topics
Categories:
Sub Categories:
Forums
Popular Tags
- #ecto
- #liveview
- #troubleshooting
- #learning-elixir
- #deployment
- #library
- #erlang
- #testing
- #genserver
- #mix
- #absinthe
- #remote-other
- #otp
- #plug
- #how-to-question
- #macros
- #postgres
- #channels
- #elixirconf
- #exunit
- #discussion
- #code-sync
- #javascript
- #podcasts
- #onsite
- #dialyzer
- #docker
- #authentication
- #umbrella
- #full-time-contract
- #podcasts-by-brainlid
- #ecto-query
- #elixir-ls
- #phoenix_html
- #iex
- #blog-post
- #graphql
- #genstage
- #ai
- #websockets
- #supervisor
- #advent-of-code
- #elixirconf-us
- #distillery
- #processes
- #forms
- #api
- #metaprogramming
- #security
- #performance









