Tesk lets you verify task invocations during tests without executing them asynchronously.
To support this, the Tesk module implements the task-spawning functions from Task (start/3 and start_link/3).
Functions such as await/2 are intentionally not implemented, since awaiting a task is usually part of the logic under test and should behave normally.
Add :tesk to your mix.exs deps:
def deps do
[
{:tesk, "~> 1.0.0"}
]
endUse Tesk wherever you would use Task.start/3 or Task.start_link/3:
Tesk.start(MyApp.Mailer, :send_welcome, [user.id])
Tesk.start_link(MyApp.Indexer, :reindex, [doc.id])The behavior of Tesk depends on the configured adapter. In tests, the Tesk.Adapters.Test adapter records scheduled tasks for assertions, which can be verified using Tesk.Assertions. The Tesk.Adapters.Inline adapter executes tasks synchronously. See "Adapters" and "Assertions" below for more information.
Tesk also provides the Tesk.Supervisor module, which implements the corresponding task-spawning functions from Task.Supervisor (start_child/5):
Tesk.Supervisor.start_child(
MyApp.TaskSupervisor,
MyApp.Mailer,
:send_welcome,
[user.id]
)See Tesk.Supervisor for more information.
By default, Tesk uses Tesk.Adapters.Task, which delegates
directly to Task and Task.Supervisor.
In tests, adapters can change how tasks are executed:
-
Tesk.Adapters.Test- captures{module, function, args}calls for assertions. SeeTesk.Assertionsfor more information. -
Tesk.Adapters.Inline- runs tasks synchronously in the caller process.
With Tesk.Adapters.Test configured, use Tesk.Assertions to
make assertions about captured tasks:
defmodule MyApp.SignupTest do
use ExUnit.Case, async: true
import Tesk.Assertions
setup :clear_tasks
test "enqueues a welcome email" do
user = %{id: 42}
user_id = user.id
MyApp.signup(user)
assert_task_count 1
# assert_task/1 (and refute_task/1) support pattern
# matching on captured tasks:
assert_task {MyApp.Mailer, :send_welcome, [42]}
assert_task {MyApp.Mailer, :send_welcome, _}
assert_task {MyApp.Mailer, :send_welcome, [^user_id]}
end
endAdapters are resolved at compile time, ensuring task behavior cannot be changed at runtime.
They can be configured with:
-
:adapter- default adapter. -
:adapters- overrides for specificmodule,{module, function}, or{module, function, arity}.
Example:
# config/test.exs
config :tesk,
adapter: Tesk.Adapters.Test,
adapters: %{
MyApp.Mailer => Tesk.Adapters.Inline,
{MyApp.Indexer, :reindex} => Tesk.Adapters.Inline,
{MyApp.Resizer, :resize, 2} => Tesk.Adapters.Inline
}Resolution proceeds from most specific to least specific:
{module, function, arity}, {module, function}, module, and
finally the default adapter.