Equality brings a better equals in the form of ====
which prints, in case of error, a tree representation of the divergence between the
compared values.
To use equality in an existing SBT project with Scala 2.12, add the following dependency to your build.sbt
:
resolvers += Resolver.bintrayRepo("io-monadplus", "maven")
libraryDependencies += "io.monadplus" %% "equality-core" % "0.0.1"
Equality is thought to be used with scalatest.
import org.scalatest.FreeSpec
import cats.implicits._
import equality.all._
class Example extends FreeSpec {
"Given an arbitrary ADT" - {
"should compare two instances" in {
sealed trait Establishment
case class Pub(name: String) extends Establishment
case class Restaurant(name: String, clientsPerTable: List[Option[Int]]) extends Establishment
case class Owner(name: String, establishment: Establishment)
val toscana = Restaurant("Toscana", List(none, 2.some, 3.some))
val andrea = Owner(name = "Andrea", establishment = toscana)
val piazza = Restaurant("Piazza", List(none, 2.some, 4.some))
val mario = Owner(name = "Mario", establishment = piazza)
andrea ==== mario
}
}
}
Output
Example:
Given an arbitrary ADT
- should compare two instances *** FAILED ***
✕ Owner$1
├── ✕ name: String [Andrea not equal to Mario]
└── ✕ establishment: Restaurant
├── ✕ name: String [Toscana not equal to Piazza]
└── ✕ clientsPerTable: List
├── ✔ 0: None
├── ✔ 1: Some
│ └── ✔ value: Integer [2]
└── ✕ 2: Some
└── ✕ value: Integer [3 not equal to 4] (eq.scala:18)
Equality is powered by shapeless for type class derivation of arbitrary ADTs. Equality can derivate instances of products (case classes) and coproducts (sealed traits + subclasses) of any combination of primitive type. It also supports scala's std collection like Option, List, Vector, Map, Set, et cetera.
Furthermore, a user can define its own instances in case of need.
In this example we are going to create an instance for NonEmptyList
import equality._
import equality.all._
import cats.data.NonEmptyList
implicit def nonEmptyListEq[A: Eq]: Eq[NonEmptyList[A]] =
new Eq[NonEmptyList[A]] {
override def compare(x: NonEmptyList[A], y: NonEmptyList[A]) =
Named(className = "NonEmptyList", fields = List(
"head" -> (x.head =><= y.head),
"tail" -> (x.tail =><= y.tail)
))
}