TestContext

final class TestContext extends ExecutionContext
A scala.concurrent.ExecutionContext implementation and a provider
of cats.effect.Timer instances, that can simulate async boundaries
and time passage, useful for testing purposes.
Usage for simulating an ExecutionContext):
{{{
implicit val ec = TestContext()
ec.execute(new Runnable { def run() = println("task1") })
ex.execute(new Runnable {
def run() = {
println("outer")
 ec.execute(new Runnable {
   def run() = println("inner")
 })
}
})
// Nothing executes until tick gets called
ec.tick()
// Testing the resulting state
assert(ec.state.tasks.isEmpty)
assert(ec.state.lastReportedFailure == None)
}}}
Our TestContext can also simulate time passage, as we are able
to builds a cats.effect.Timer instance for any data type that
has a LiftIO instance:
{{{
val ctx = TestContext()
val timer: Timer[IO] = ctx.timer[IO]
}}}
We can now simulate actual time:
{{{
val io = timer.sleep(10.seconds) *> IO(1 + 1)
val f = io.unsafeToFuture()
// This invariant holds true, because our IO is async
assert(f.value == None)
// Not yet completed, because this does not simulate time passing:
ctx.tick()
assert(f.value == None)
// Simulating time passing:
ctx.tick(10.seconds)
assert(f.value == Some(Success(2))
}}}
Simulating time makes this pretty useful for testing race conditions:
{{{
val never = IO.async[Int] (_ => {})
val timeoutError = new TimeoutException
val timeout = timer.sleep(10.seconds) *> IO.raiseErrorInt
val pair = (never, timeout).parMapN(_ + _)
// Not yet
ctx.tick()
assert(f.value == None)
// Not yet
ctx.tick(5.seconds)
assert(f.value == None)
// Good to go:
ctx.tick(5.seconds)
assert(f.value, Some(Failure(timeoutError)))
}}}
Companion
object
trait ExecutionContext
class Object
trait Matchable
class Any

Value members

Methods

def execute(r: Runnable): Unit
Inherited from ExecutionContext, schedules a runnable
for execution.
def reportFailure(cause: Throwable): Unit
Inherited from ExecutionContext, reports uncaught errors.
def state: State
Returns the internal state of the TestContext, useful for testing
that certain execution conditions have been met.
def tickOne(): Boolean
Executes just one tick, one task, from the internal queue, useful
for testing that a some runnable will definitely be executed next.
Returns a boolean indicating that tasks were available and that
the head of the queue has been executed, so normally you have
this equivalence:
{{{
while (ec.tickOne()) {}
// ... is equivalent with:
ec.tick()
}}}
Note that ask extraction has a random factor, the behavior being like
tick, in order to simulate nondeterminism. So you can't rely on
some ordering of execution if multiple tasks are waiting execution.
Returns
true if a task was available in the internal queue, and
was executed, or false otherwise
def tick(time: FiniteDuration): Unit
Triggers execution by going through the queue of scheduled tasks and
executing them all, until no tasks remain in the queue to execute.
Order of execution isn't guaranteed, the queued Runnables are
being shuffled in order to simulate the needed nondeterminism
that happens with multi-threading.
{{{
implicit val ec = TestContext()
val f = Future(1 + 1).flatMap(_ + 1)
// Execution is momentarily suspended in TestContext
assert(f.value == None)
// Simulating async execution:
ec.tick()
assert(f.value, Some(Success(2)))
}}}
Value Params
time
is an optional parameter for simulating time passing;
def tickAll(time: FiniteDuration): Unit
def schedule(delay: FiniteDuration, r: Runnable): () => Unit
def derive(): ExecutionContext
def now(): FiniteDuration

Inherited methods

@deprecated("preparation of ExecutionContexts will be removed", "2.12.0")
def prepare(): ExecutionContext
Inhertied from
ExecutionContext