cats.effect.testkit
package cats.effect.testkit
Type members
Classlikes
trait AsyncGenerators[F <: ([_$21] =>> Any)] extends GenTemporalGenerators[F, Throwable] with SyncGenerators[F]
trait GenTemporalGenerators[F <: ([_$20] =>> Any), E] extends GenSpawnGenerators[F, E] with ClockGenerators[F]
trait MonadErrorGenerators[F <: ([_$9] =>> Any), E] extends MonadGenerators[F] with ApplicativeErrorGenerators[F, E]
trait SyncGenerators[F <: ([_$13] =>> Any)] extends MonadErrorGenerators[F, Throwable] with ClockGenerators[F]
A
of
and time passage, useful for testing purposes.
scala.concurrent.ExecutionContext
implementation and a providerof
cats.effect.Timer
instances, that can simulate async boundariesand time passage, useful for testing purposes.
Usage for simulating an
{{{
implicit val ec = TestContext()
ExecutionContext
):{{{
implicit val ec = TestContext()
ec.execute(new Runnable { def run() = println("task1") })
ex.execute(new Runnable {
def run() = {
println("outer")
def run() = {
println("outer")
ec.execute(new Runnable {
def run() = println("inner")
})
}
})
})
// Nothing executes until
ec.tick()
tick
gets calledec.tick()
// Testing the resulting state
assert(ec.state.tasks.isEmpty)
assert(ec.state.lastReportedFailure == None)
}}}
assert(ec.state.tasks.isEmpty)
assert(ec.state.lastReportedFailure == None)
}}}
Our
to builds a
has a
TestContext
can also simulate time passage, as we are ableto builds a
cats.effect.Timer
instance for any data type thathas a
LiftIO
instance:{{{
val ctx = TestContext()
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()
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)
assert(f.value == None)
// Not yet completed, because this does not simulate time passing:
ctx.tick()
assert(f.value == None)
ctx.tick()
assert(f.value == None)
// Simulating time passing:
ctx.tick(10.seconds)
assert(f.value == Some(Success(2))
}}}
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 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)
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)))
}}}
ctx.tick(5.seconds)
assert(f.value, Some(Failure(timeoutError)))
}}}
- Companion
- object