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.raiseError[Int](timeoutError) 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)))
- Self Type
- TestContext
- Source
- TestContext.scala
- Alphabetic
- By Inheritance
- TestContext
- ExecutionContext
- AnyRef
- Any
- Hide All
- Show All
- Public
- Protected
Value Members
- final def !=(arg0: Any): Boolean
- Definition Classes
- AnyRef → Any
- final def ##: Int
- Definition Classes
- AnyRef → Any
- final def ==(arg0: Any): Boolean
- Definition Classes
- AnyRef → Any
- final def asInstanceOf[T0]: T0
- Definition Classes
- Any
- def clone(): AnyRef
- Attributes
- protected[lang]
- Definition Classes
- AnyRef
- Annotations
- @throws(classOf[java.lang.CloneNotSupportedException]) @native()
- def contextShift[F[_]](implicit F: Async[F]): ContextShift[F]
Derives a
cats.effect.ContextShift
from thisTestContext
, for any data type that has anAsync
instance.Derives a
cats.effect.ContextShift
from thisTestContext
, for any data type that has anAsync
instance.Example:
val ctx = TestContext() // Building a Timer[IO] from this: implicit val timer: Timer[IO] = ctx.timer[IO] // Can now simulate 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))
- final def eq(arg0: AnyRef): Boolean
- Definition Classes
- AnyRef
- def equals(arg0: AnyRef): Boolean
- Definition Classes
- AnyRef → Any
- def execute(r: Runnable): Unit
Inherited from
ExecutionContext
, schedules a runnable for execution.Inherited from
ExecutionContext
, schedules a runnable for execution.- Definition Classes
- TestContext → ExecutionContext
- def finalize(): Unit
- Attributes
- protected[lang]
- Definition Classes
- AnyRef
- Annotations
- @throws(classOf[java.lang.Throwable])
- final def getClass(): Class[_ <: AnyRef]
- Definition Classes
- AnyRef → Any
- Annotations
- @native()
- def hashCode(): Int
- Definition Classes
- AnyRef → Any
- Annotations
- @native()
- def ioContextShift: ContextShift[IO]
Derives a
cats.effect.ContextShift
from thisTestContext
forIO
. - def ioTimer: Timer[IO]
Derives a
cats.effect.Timer
from thisTestContext
forIO
. - final def isInstanceOf[T0]: Boolean
- Definition Classes
- Any
- final def ne(arg0: AnyRef): Boolean
- Definition Classes
- AnyRef
- final def notify(): Unit
- Definition Classes
- AnyRef
- Annotations
- @native()
- final def notifyAll(): Unit
- Definition Classes
- AnyRef
- Annotations
- @native()
- def reportFailure(cause: Throwable): Unit
Inherited from
ExecutionContext
, reports uncaught errors.Inherited from
ExecutionContext
, reports uncaught errors.- Definition Classes
- TestContext → ExecutionContext
- def state: State
Returns the internal state of the
TestContext
, useful for testing that certain execution conditions have been met. - final def synchronized[T0](arg0: => T0): T0
- Definition Classes
- AnyRef
- def tick(time: FiniteDuration = Duration.Zero): Unit
Triggers execution by going through the queue of scheduled tasks and executing them all, until no tasks remain in the queue to execute.
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
Runnable
s are being shuffled in order to simulate the needed non-determinism 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)))
The optional parameter can be used for simulating time, to be used in combination with
cats.effect.Timer
. See the timer method.Example:
val ctx = TestContext() // Building a Timer[IO] from this: implicit val timer: Timer[IO] = ctx.timer[IO] // Can now simulate 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))
- time
is an optional parameter for simulating time passing;
- 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.
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 non-determinism. 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, orfalse
otherwise
- def timer[F[_]](implicit F: LiftIO[F]): Timer[F]
Derives a
cats.effect.Timer
from thisTestContext
, for any data type that has aLiftIO
instance.Derives a
cats.effect.Timer
from thisTestContext
, for any data type that has aLiftIO
instance.Example:
val ctx = TestContext() // Building a Timer[IO] from this: implicit val timer: Timer[IO] = ctx.timer[IO] // Can now simulate 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))
- def toString(): String
- Definition Classes
- AnyRef → Any
- final def wait(): Unit
- Definition Classes
- AnyRef
- Annotations
- @throws(classOf[java.lang.InterruptedException])
- final def wait(arg0: Long, arg1: Int): Unit
- Definition Classes
- AnyRef
- Annotations
- @throws(classOf[java.lang.InterruptedException])
- final def wait(arg0: Long): Unit
- Definition Classes
- AnyRef
- Annotations
- @throws(classOf[java.lang.InterruptedException]) @native()
Deprecated Value Members
- def prepare(): ExecutionContext
- Definition Classes
- ExecutionContext
- Annotations
- @deprecated
- Deprecated
(Since version 2.12.0) preparation of ExecutionContexts will be removed