cats.effect.kernel.testkit
package cats.effect.kernel.testkit
Type members
Classlikes
trait MonadErrorGenerators[F[_], E] extends MonadGenerators[F] with ApplicativeErrorGenerators[F, E]
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.
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)))
- Companion
- object