cats.effect.testkit

Type members

Classlikes

trait ApplicativeErrorGenerators[F <: ([_$7] =>> Any), E] extends ApplicativeGenerators[F]
trait ApplicativeGenerators[F <: ([_$4] =>> Any)] extends Generators1[F]
trait AsyncGenerators[F <: ([_$21] =>> Any)] extends GenTemporalGenerators[F, Throwable] with SyncGenerators[F]
trait ClockGenerators[F <: ([_$10] =>> Any)] extends Generators1[F]
trait FreeSyncEq
trait GenK[F <: ([_$1] =>> Any)]
trait GenSpawnGenerators[F <: ([_$18] =>> Any), E] extends MonadCancelGenerators[F, E]
trait GenTemporalGenerators[F <: ([_$20] =>> Any), E] extends GenSpawnGenerators[F, E] with ClockGenerators[F]
trait Generators1[F <: ([_$2] =>> Any)]
trait MonadCancelGenerators[F <: ([_$15] =>> Any), E] extends MonadErrorGenerators[F, E]
trait MonadErrorGenerators[F <: ([_$9] =>> Any), E] extends MonadGenerators[F] with ApplicativeErrorGenerators[F, E]
trait MonadGenerators[F <: ([_$6] =>> Any)] extends ApplicativeGenerators[F]
trait SyncGenerators[F <: ([_$13] =>> Any)] extends MonadErrorGenerators[F, Throwable] with ClockGenerators[F]
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
object TestContext
Companion
class
final case class TestException(i: Int) extends Exception
final class Time
Companion
object
object Time
Companion
class
object TimeT
object freeEval extends FreeSyncEq
object pure

Types

type TimeT[F <: ([_$1] =>> Any), A] = Kleisli[F, Time, A]