Alejandro Echeverri
Alejandro Echeverri

Reputation: 1358

cats `Eq`'s `===` with Scalatest

I was reading Scala with Cats and in the first chapter of the book, they describe Eq usage.

One of the exercises is about writing an instance of that typeclass for validating the equality of two cats. Consider this:

Model

final case class Cat(name: String, age: Int, color: String)

Typeclass instance

import cats.Eq
import cats.syntax.eq._
import cats.instances.string._
import cats.instances.int._
import co.alejandrome.typeclass.model.Cat

object CatsEqInstances {

  implicit val catsEq: Eq[Cat] = Eq.instance[Cat] {
    (cata, catb) =>
      cata.color === catb.color && cata.age === catb.age && cata.name === catb.name
  }

}

Unit tests

import co.alejandrome.typeclass.model.Cat
import org.scalatest.{FlatSpec, Matchers}
import cats.instances.option._

class EqTest extends FlatSpec with Matchers{

  import CatsEqInstances._

  "Eq" should "compare two cats" in {
    val cat1 = Cat("Garfield",   38, "orange and black")
    val cat2 = Cat("Heathcliff", 33, "orange and black")

    catsEq.eqv(cat1, cat2) shouldBe false

    val cat3 = Cat("Garfield",   38, "orange and black")

    catsEq.eqv(cat1, cat3) shouldBe true
  }

  it should "compare two Optional cats" in {
    val cat1 = Cat("Garfield",   38, "orange and black")
    //val cat2 = Cat("Heathcliff", 33, "orange and black")

    val optionCat1 = Option(cat1)
    val optionCat2 = Option.empty[Cat]

    optionCat1 === optionCat2
  }

}

The first test works fine, but the second one doesn't because === operator is the Scalatest one, not the Eq one.

I explored if the typeclass has any syntax or implicit for this but I cannot find anything about it. I saw in this PR that there's a solution for this, based on Bill Venner's solution, but this is only for cats own unit tests.

I was poking with EqSyntax trait but I can't figure it out how to override scalatest own implementation of ===.

Is there any way to do this? Or is this not implemented yet?

Thanks

Upvotes: 2

Views: 1426

Answers (1)

SergGr
SergGr

Reputation: 23788

I'm not sure if I understand your problem correctly, but it looks like you can re-use what Cats implemented for their own tests basing on the PR you referenced. Just change your test class definition to

class EqTest extends FlatSpec with Matchers with cats.tests.StrictCatsEquality  {

and then you can write tests such as

optionCat1 === optionCat2 shouldBe false

or

optionCat1 should !== (optionCat2)

and they will you use your Eq[Cat] definition (if it is imported into the scope).

Essentially the idea is that ScalaTest === uses some implicit such as CanEqual. So if you provide (import) your custom implicit implementation - you can change how === works inside.

Upvotes: 3

Related Questions