Introduction

Difflicious is a library that produces nice readable diffs in your tests.

  • Readable and Actionable diffs
  • Customizability: supporting all kinds of tweaks you’d want to do such as ignoring fields or compare lists independent of element order.

Here’s a motivational example!

import difflicious._
import difflicious.implicits._

sealed trait HousePet {
  def name: String
}
object HousePet {
  final case class Dog(name: String, age: Int) extends HousePet
  final case class Cat(name: String, livesLeft: Int) extends HousePet
  
  implicit val differ: Differ[HousePet] = Differ.derived
}

import HousePet.{Cat, Dog}

val petsDiffer = Differ[List[HousePet]]
  .pairBy(_.name)                          // Match pets in the list by name for comparison
  .ignoreAt(_.each.subType[Cat].livesLeft) // Don't worry about livesLeft for cats when comparing
  
petsDiffer.diff(
  obtained = List(
    Dog("Andy", 12),
    Cat("Dr.Evil", 8),
    Dog("Lucky", 5),
  ),
  expected = List(
    Dog("Lucky", 6),
    Cat("Dr.Evil", 9),
    Cat("Andy", 12),
  )
)

And this is the diffs you will see:

List(
  Dog != Cat
  === Obtained ===
  Dog(
    name: "Andy",
    age: 12,
  )
  === Expected ===
  Cat(
    name: "Andy",
    livesLeft: [IGNORED],
  ),
  Cat(
    name: "Dr.Evil",
    livesLeft: [IGNORED],
  ),
  Dog(
    name: "Lucky",
    age: 5 -> 6,
  ),
)

In the example, we can see that:

  • Difflicious spots that Andy is not a Dog but instead a Cat!!
  • The cat Dr.Evil is considered to be the same on both sides, because we decided to not check how many lives the cats have left.
  • A diff is produced showing us that Lucky’s age is wrong.