I have a misunderstanding with exception handling in ZIO. I followed the ZIO-Documentation.
In a test class I run the following code:
new DefaultRuntime {}.unsafeRun(
(for {
peopleRef <- Ref.make(Vector(People()))
_ <- people(2).provide(Test(peopleRef)) // this throws the exception
} yield ())
.fold(
err =>
err shouldBe ServiceException("No People with id 2"),
_ => fail("line above should fail")
)
)
I thought with the fold function I could handle this exception, but it does not even get there. I get this Exception on the console:
Fiber failed.
An unchecked error was produced.
pme123.zio.examples.swapi.package$ServiceException
at pme123.zio.examples.swapi.Swapi$Test$$anon$4.$anonfun$people$6(Swapi.scala:57)
at zio.ZIO$MapFn.apply(ZIO.scala:2590)
at zio.ZIO$MapFn.apply(ZIO.scala:2588)
at zio.internal.FiberContext.evaluateNow(FiberContext.scala:709)
at zio.Runtime.unsafeRunAsync(Runtime.scala:93)
....
What do I miss?
Here is a minimal example:
object MyApp
extends App {
def run(args: List[String]): ZIO[Environment, Nothing, Int] =
program
.fold({ error => 1 // this is not reached
}, _ => 0)
private lazy val program = for {
peopleRef <- Ref.make(Vector(22))
_ <- Test(peopleRef).people(12)
} yield ()
case class Test(ref: Ref[Vector[Int]]) {
def people(id: Int): Task[Int] =
for {
ints <- ref.get
} yield ints.find(_ == id) match {
case None => throw new IllegalArgumentException(s"No People with id $id") // this is thrown
case Some(p) => p
}
}
}
Here is the whole code: SwapiTest.scala
ZIO doesn't catch exception unless you wrap it in ZIO. You can use ZIO.effect to wrap the entire block of legacy unsafe code throwing exceptions or just use IO.fail for specific exception.
I refactored your code to be more ZIO-like and produce failed Task instead of throwing exception:
case class Test(ref: Ref[Vector[Int]]) {
def people(id: Int):Task[Int]=
for {
ints <- ref.get
int <- ZIO.effectTotal(ints.find(_ == id))
res <- int match {
case None => IO.fail(new IllegalArgumentException(s"No People with id $id"))
case Some(p) => ZIO.effectTotal(p)
}
} yield res
}
In order to catch IllegalArgumentException you need the following fold:
...
.fold(
err => err shouldBe IllegalArgumentException("No People with id 2"),
_ => fail("line above should fail")
)
ServiceException should not appear here as nothing throwing it.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With