scalafunctional-programmingscala-catsspecs2cats-effect

Matchers for cats EitherT and effects


I'm new in cats and functional programming and I'm struggling with unit testing functional data types like EitherT. Having example code:

class Library[F[_]]() {
    def create(book: Book)(implicit M: Monad[F]): EitherT[F, BookAlreadyExistsError, Book] = ...
}

I'd like to test it using Spec2 but I don't know how to do it properly. Tried something like this but it does not work:

  val library = Library[IO]()

  test("create book") {

    val book = Book("Title 1", 2016, "author 1")
    (for (
      resultBook <- library.create(book)
    ) yield resultBook shouldEqual ???
    ).unsafeRunSync()

  }

I'd like to have very simple assertions like this:

   resultBook shouldEqual Right(Book("Title 1", 2016, "author 1"))
   // or
   resultBook shouldEqual Left(BookAlreadyExistsError)

Solution

  • specs2-cats provides IOMatchers trait which enables the following syntax

    library.create(book).value must returnValue(Right(book))
    

    where

    libraryDependencies += "org.specs2" %% "specs2-core" % "4.8.1" % Test,
    libraryDependencies += "org.specs2" %% "specs2-cats" % "4.8.1" % Test,
    

    Here is a working example

    import cats.data.EitherT
    import cats.effect.IO
    import org.specs2.mutable.Specification
    import org.specs2.matcher.IOMatchers
    
    class CatsSpec extends Specification with IOMatchers {
      case class Book(title: String, year: Int, author: String)
      def create(book: Book): EitherT[IO, String, Book] = EitherT(IO(Right(book).withLeft[String]))
      val book = Book("Title 1", 2016, "author 1")
    
      "specs2-cats dependency" should {
        "provide matcher for IO effect" in {
          create(book).value must returnValue(Right(book))
        }
      }
    }