Testing Queues
View SourceWhere workers are the primary "unit" of an Oban system, queues are the "integration" point between
the database and your application. That means to test queues and the jobs within them, your tests
will have to interact with the database. To simplify that interaction, reduce boilerplate, and
make assertions more expressive Oban.Testing
provides a variety of helpers.
Asserting Enqueued Jobs
During test runs you don't typically want to execute jobs. Rather, you need to verify that the job
was enqueued properly. With the recommended test setup queues and plugins are disabled, and jobs
won't be inserted into the database at all. Instead, they'll be executed immediately within the
calling process. The Oban.Testing.assert_enqueued/2
and Oban.Testing.refute_enqueued/2
helpers
simplify running queries to check for those available
or scheduled
jobs sitting in the
database.
Let's look at an example where we want to check that an activation job is enqueued after a user signs up:
test "scheduling activation upon sign up" do
{:ok, account} = MyApp.Account.sign_up(email: "parker@example.com")
assert_enqueued worker: MyApp.ActivationWorker, args: %{id: account.id}, queue: :default
end
It's also possible to assert that job args
or meta
have a particular shape, without matching
exact values:
test "enqueued args have a particular key" do
:ok = MyApp.Account.notify_owners(account())
assert_enqueued queue: :default, args: %{email: _}
end
You can also refute that a job was enqueued. The refute_enqueued
helper takes the same arguments
as assert_enqueued
, though you should take care to be as unspecific as possible.
Building on the example above, let's refute that a job is enqueued when account sign up fails:
test "bypassing activation when sign up fails" do
{:error, _reason} = MyApp.Account.sign_up(email: "parker@example.com")
refute_enqueued worker: MyApp.ActivationWorker
end
Asserting Multiple Jobs
Asserting and refuting about a single job isn't always enough. Sometimes you need to check for
multiple jobs at once, or perform more complex assertions on the jobs themselves. In that
situation, you can use all_enqueued
instead.
The first example we'll look at asserts that multiple jobs from the same worker are enqueued all at once:
test "enqueuing one job for each child record" do
:ok = MyApp.Account.notify_owners(account())
assert jobs = all_enqueued(worker: MyApp.NotificationWorker)
assert 3 == length(jobs)
end
The enqueued
helpers all build dynamic queries to check for jobs within the
database. Dynamic queries don't work for complex objects with nested values or a
partial set of keys. In that case, you can use all_enqueued
to pull jobs into
your tests and use the full power of pattern matching for assertions.
test "enqueued jobs have args that match a particular pattern" do
:ok = MyApp.Account.notify_owners(account())
for job <- all_enqueued(queue: :default) do
assert %{"email" => _, "avatar" => %{"url" => _}} = job.args
end
end
Integration Testing Queues
During integration tests it may be necessary to run jobs because they do work essential for the
test to complete, i.e. sending an email, processing media, etc. You can execute all available jobs
in a particular queue by calling Oban.drain_queue/1,2
directly from your tests.
For example, to process all pending jobs in the "mailer" queue while testing some business logic:
defmodule MyApp.BusinessTest do
use MyApp.DataCase, async: true
alias MyApp.{Business, Worker}
test "we stay in the business of doing business" do
:ok = Business.schedule_a_meeting(%{email: "monty@brewster.com"})
assert %{success: 1, failure: 0} = Oban.drain_queue(queue: :mailer)
# Now, make an assertion about the email delivery
end
end
See Oban.drain_queue/1,2
for a myriad of options and additional details.