View Source Sandbox Testing
When testing your application, you may not want to actually push/pull jobs to Faktory, but instead rely on asserting that jobs would be enqueued as you expect. Enabling sandbox mode will accomodate this; when sandbox mode is active, the following changes are made:
- no connection will be made to Faktory (you don't even need a Faktory server running!)
- jobs enqueued (with
perform_async/2
) will never be run - jobs enqueued will be recorded, and can be viewed using
FaktoryWorker.Sandbox
To enable sandbox mode, set :sandbox
to true
:
# config/test.exs
config :my_app, FaktoryWorker, sandbox: true
# lib/my_app/application.ex
defmodule MyApp.Application do
@moduledoc false
use Application
def start(_type, _args) do
children = [
{FaktoryWorker, Application.get_env(:my_app, FaktoryWorker)}
]
opts = [name: MyApp.Supervisor, strategy: :one_for_one]
Supervisor.start_link(children, opts)
end
end
You can verify that sandbox mode is enabled with FaktoryWorker.Sandbox.active?/0
.
Writing Tests
To aid in tests that rely on jobs being enqueued, as well as tests for job modules
themselves, you can use the helpers in FaktoryWorker.Testing
. Below is a simple
test for a module that enqueues a job:
defmodule MyApp.QueueTest do
use ExUnit.Case
import FaktoryWorker.Testing
setup :reset_queues
test "perform/1" do
MyApp.Job.perform_async("hello, world!")
assert_enqueued MyApp.Job, args: ["hello, world!"]
refute_enqueued MyApp.Job, args: ["goodbye!"]
end
test "multiple enqueues" do
for _ <- 1..10 do
MyApp.Job.perform_async("brrrrr")
end
assert_enqueued MyApp.Job, count: 10
end
test "perform/2" do
MyApp.Job.perform_async(["foo", "bar"])
assert_enqueued MyApp.Job, args: ["foo", "bar"]
end
test "with opts" do
MyApp.Job.perform_async("howdy", reserve_for: 1_500)
assert_enqueued MyApp.Job, opts: [reserve_for: 1_500]
# these would work too!
# assert_enqueued MyApp.Job, args: ["howdy"]
# assert_enqueued MyApp.Job, args: ["howdy"], opts: [reserve_for: 1_500]
end
end
Here's a simple example test for a job module itself:
defmodule MyApp.JobTest do
use ExUnit.Case
import FaktoryWorker.Testing
test "handles no arguments" do
assert perform_job(MyApp.Job) == :ok
end
test "handles strings" do
assert perform_job(MyApp.Job, "foo") == :ok
end
test "handles maps" do
assert perform_job(MyApp.Job, %{id: 1}) == {:ok, "cool, a map!"}
end
test "handles structs" do
# structs are serialized and passed to `perform` as bare maps
assert perform_job(MyApp.Job, %MyApp.User{id: 1}) == {:ok, "cool, a map!"}
end
test "doesn't handle numbers" do
assert perform_job(MyApp.Job, 1_234) == {:error, "boo, no numbers!"}
end
test "gracefully handles raises" do
assert perform_job(MyApp.Job, "I'll cause a raise!") == {:error, "raise reason"}
end
end
When you can, prefer to use perform_job/3
instead of calling perform
directly, as
it will ensure that the code under test is run in the same way that it will eventually
be at runtime.