Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to test an exception case with zio-test

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")))
    }
  )
like image 502
pme Avatar asked Nov 01 '19 16:11

pme


5 Answers

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")))
        }
      )
    )
like image 119
Adam Fraser Avatar answered Oct 16 '22 18:10

Adam Fraser


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.

like image 43
pme Avatar answered Oct 16 '22 18:10

pme


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"))
        }
      )
    )
like image 45
Ruurtjan Pul Avatar answered Oct 04 '22 08:10

Ruurtjan Pul


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")))
    }
  )
}
like image 7
Landlocked Surfer Avatar answered Oct 16 '22 18:10

Landlocked Surfer


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)))
        }
      )
    )
like image 5
Mohammad Reza Esmaeilzadeh Avatar answered Oct 16 '22 17:10

Mohammad Reza Esmaeilzadeh