Setup for Async Testing
PJUllrich opened this issue · 2 comments
I have problems running tests asynchonously (that is in parallel) using the EventStore. I was wondering whether it is possible at all to set up the library in a way that supports async tests.
Currently, whenever I have more than around 5 tests that use the EventStore library for testing, they run into race conditions over the database connection with errors like this:
** (exit) exited in: DBConnection.Holder.checkout(EventStore.Postgrex, [timeout: 15000])
** (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
My current setup tags
function in my test cases (e.g. DataCase
) looks like this:
setup tags do
:ok = Sandbox.checkout(MyApp.Repo)
unless tags[:async] do
Sandbox.mode(MyApp.Repo, {:shared, self()})
end
config = EventStore.Config.parsed()
postgrex_config = EventStore.Config.default_postgrex_opts(config)
{:ok, eventstore_connection} = Postgrex.start_link(postgrex_config)
Storage.Initializer.reset!(eventstore_connection)
{:ok, _} = Application.ensure_all_started(:eventstore)
on_exit(fn ->
Application.stop(:eventstore)
Process.exit(eventstore_connection, :shutdown)
end)
end
:ok
end
EventStore can be used for async tests as long as you don't attempt to reset the database schema between test runs. The issue you have is that Storage.Initializer.reset!/1
will truncate the tables being used by any EventStore operations running concurrently.
I'd suggest one of these approaches:
- Run your EventStore tests synchronously with
async: false
. - Write your tests so that they can run async, but without affecting each other, and don't reset the database (or do it only once at the start or end of running the entire test suite). As an example you could prefix a random string to the stream name for each test and then only subscribe to or look for events associated with the test-specific streams (e.g. prefix stream identity
1234
astest-foo-1234
). - The next EventStore release has support for Postgres schemas (#182) and starting dynamic event store (#184) instances. These features could be used to configure and run a separate EventStore instance per test, each with its own unique schema to ensure the test data remains isolated.
Testing with Commanded
Commanded provides an in-memory event store adapter which can be used for testing.
- Use the in-memory event store which is a
GenServer
process. Dynamic Commanded applications (#324), in the next Commanded release, will support isolation for async tests as a separate Commanded application and in-memory event store can be started per test. - Define two mix environments and have different event store adapters configured in each. Use the
:test
environment to configure the in-memory event store. Add a new:integration
mix environment configured to use the Postgres EventStore. Then configured the tests to run async in test on (e.g.async: Mix.env() == :test
). This gives you the option to run quickly locally (async in:test
env), but have the same tests run as integration tests in CI or on demand (sync in:integration
env).
Thank you! Then I'll wait for the next release and consider which option suits my requirements best :)