Reputation: 14803
I have the following function, that I want to test:
def people(id: Int): RIO[R, People]
This function returns People if there is one for that id
, resp. fails if not, like:
IO.fail(ServiceException(s"No People with id $id"))
The happy case works, like:
suite("Get a Person for an ID") (
testM("get Luke Skywalker") {
for {
peopleRef <- Ref.make(Vector(People()))
luke <- Swapi.>.people(1).provide(Test(peopleRef))
} yield assert(luke, equalTo(People()))
},
But how can I test the failure case? I tried different things, mostly the types do not match. Here is an attempt:
testM("get not existing People") {
(for {
peopleRef <- Ref.make(Vector(People()))
failure = Swapi.>.people(2).provide(Test(peopleRef))
} yield assertM(failure, fail(Cause.die(ServiceException(s"No People with id 2")))
}
)
Upvotes: 11
Views: 7156
Reputation: 1721
So I decided none of the answers were as clear as I wanted and chat gpt was using scala test mixed with ZIO (LOL) (ZIO 2.0
)
I believe the questioner is asking no only to see if it failed but also assert the message is what they expect. The big assertions then are
fails
and hasMessage
(coupled with equalTo
). Would be nice if the documentation for had more examples like this, until then I hope this helps
import zio.{Scope, ZIO}
import zio.test.Assertion.{equalTo, fails, hasMessage}
import zio.test.{Spec, TestEnvironment, ZIOSpecDefault, assertZIO}
object ExampleSpec extends ZIOSpecDefault{
override def spec: Spec[TestEnvironment with Scope, Any] = {
suite("mapUpdatedMediaElements") {
test("empty map of responses") {
assertZIO(ZIO.fail(new RuntimeException("BROK3N")).exit)(fails(hasMessage(equalTo("BROK3N"))))
}
}
}
}
Upvotes: 0
Reputation: 433
Here's another compact variant using assertM
for ZIO 1.0:
import zio._
import zio.test.Assertion.{equalTo, fails}
import zio.test._
object ExampleSpec extends DefaultRunnableSpec {
def spec = suite("ExampleSpec")(
testM("Example of testing for expected failure") {
assertM(ZIO.fail("fail").run)(fails(equalTo("fail")))
}
)
}
In ZIO 2.0 the test looks very similar:
import zio._
import zio.test._
import zio.test.Assertion.{equalTo, fails}
object ExampleSpec extends ZIOSpecDefault {
def spec = suite("ExampleSpec ZIO 2.0")(
test("Example of testing for expected failure in ZIO 2.0") {
assertZIO(ZIO.fail("fail").exit)(fails(equalTo("fail")))
}
)
}
Upvotes: 8
Reputation: 55760
And for ZIO 2.0 there are a few changes:
exit
instead of run
test
instead of testM
assert
has a new currying syntax of assert(result)(assertion)
import zio.test._
import zio.test.Assertion._
import zio.ZIO
object ExampleSpec extends DefaultRunnableSpec(
suite("ExampleSpec")(
test("Example of testing for expected failure") {
for {
result <- ZIO.fail("failureResult").exit
} yield assert(result)(
fails(equalTo("failureResult"))
)
}
)
)
Upvotes: 11
Reputation: 1341
if your error is throwable, the equalsTo
fails when it runs against a running effect so you have to use isSubtype
Assertion in order to check do you receive your correct error and it is a little More tricky:
import zio.test._
import zio.test.Assertion._
import zio.ZIO
object ExampleSpec
extends DefaultRunnableSpec(
suite("ExampleSpec")(
testM("Example of testing for expected failure") {
for {
result <- ZIO.fail(new NoSuchElementException).run
} yield assert(result, fails(isSubtype[NoSuchElementException](anything)))
}
)
)
Upvotes: 8
Reputation: 1377
You could also flip the error and result channels:
import zio.test._
import zio.test.Assertion._
import zio.ZIO
object ExampleSpec
extends DefaultRunnableSpec(
suite("ExampleSpec")(
testM("Example of testing for expected failure") {
for {
result <- ZIO.fail("fail").flip
} yield assert(result, equalTo("fail"))
}
)
)
Upvotes: 5
Reputation: 849
I think you've definitely got it. The only thing I would add for others with similar questions is your example also involves an environment type, which is a great topic for discussion but somewhat independent of how to test that an effect fails as expected using ZIO Test.
I've included below a minimal example of how to test that an effect fails as expected. As mentioned above, you would call run
on the effect to get an exit value and then use Assertion.fails
to assert that the effect fails with a checked exception, Assertion.dies
to assert that the effect fails with an unchecked exception, or Assertion.interrupted
to test that an effect was interrupted.
Also note that you do not have to use include equalTo("fail")
. If all you care about is that the effect failed you can just use fails(anything)
. If you are testing that an effect dies with a specified exception you can do something like dies(isSubtype[IllegalArgumentException])
.
Hope this helps!
import zio.test._
import zio.test.Assertion._
import zio.ZIO
object ExampleSpec
extends DefaultRunnableSpec(
suite("ExampleSpec")(
testM("Example of testing for expected failure") {
for {
result <- ZIO.fail("fail")
} yield assert(result, fails(equalTo("fail")))
}
)
)
Upvotes: 8
Reputation: 14803
With the help of @adamfraser in the ZIO-Chat:
Basically call run on your failing effect and then assert that it is a failure with Assertion.fails. Or Assertion.dies if it is an unchecked exception.
I think I have now a nice solution.
testM("get not existing People") {
for {
peopleRef <- Ref.make(Vector(People()))
failure <- Swapi.>.people(2).provide(Test(peopleRef)).run
} yield assert(
failure,
fails(equalTo(ServiceException("No People with id 2")))
)
}
Other solutions are still welcome.
Upvotes: 6