A few days ago I found out how easy it is to test Scalaz type class instances with specs2 and ScalaCheck. It was impressive to see that it only required a few additional lines of code in my test class to check that my monoid not only has a Monoid instance but that it actually satisfies the monoid laws.

In this post I want to show a minimal monoid example and what is needed for testing the monoid laws. Let me briefly recap the monoid laws and why you want to test them. As Wikipedia will tell you, a monoid is a set, S, together with a binary operation "•" (called append by Scalaz) that satisfies the following three axioms:

Closure: For all a, b in S, the result of the operation a • b is also in S.

Associativity: For all a, b and c in S, the equation (a • b) • c = a • (b • c) holds.

Identity element: There exists an element e (called zero by Scalaz) in S, such that for all elements a in S, the equation e • a = a • e = a holds.

In any implementation of Monoid[S] only closure is guaranteed automatically by the compiler because of the signature of append (def append(a: S, b: => S): S). But if append is really associative or if zero is really the identity element under append is not guaranteed at all. To make sure that your Monoid is actually a monoid, you could either prove the laws for your current implementation (and prove them again if you change the implementation) or you could test if those laws hold for some random values. And this is where Scalaz, specs2, and ScalaCheck play nicely together and make your law testing a lot easier!

Let's look at the example code. We start with the class for which we'll add a Monoid instance:

case class Vec(x: Int, y: Int)

Vec is a simple container of two integers and our Monoid[Vec] provides componentwise addition:

import scalaz._

object Vec {
  implicit object VecMonoid extends Monoid[Vec] {
    def zero: Vec = Vec(0, 0)
    def append(v1: Vec, v2: => Vec): Vec = Vec(v1.x + v2.x, v1.y + v2.y)
  }

  implicit object VecEqual extends Equal[Vec] {
    def equal(v1: Vec, v2: Vec): Boolean = v1 == v2
  }
}

Equal[Vec], which can test two Vecs for equality, is required for the law testing.

The next step is to write the actual test code. ScalaCheck requires an implicit Arbitrary[Vec] in order to generate random Vec values. Once we have that, we ask scalaz-specs2 to check all monoid laws for Vec in one line of code (and while we are at it, we also check the equal laws):

import org.scalacheck.Arbitrary
import org.specs2.scalaz._
import scalaz.scalacheck.ScalazProperties._

class VecSpec extends Spec {
  implicit val arbitraryVec = Arbitrary {
    for {
      (x, y) <- Arbitrary.arbitrary[(Int, Int)]
    } yield Vec(x, y)
  }

  checkAll(monoid.laws[Vec])
  checkAll(equal.laws[Vec])
}

Et voilà! That is all it takes to test the laws for Monoid[Vec]. Simple, isn't it? If we run VecSpec, we'll get the following output:

VecSpec

monoid must satisfy
+ monoid.semigroup.associative
+ monoid.left identity
+ monoid.right identity

equal must satisfy
+ equal.commutativity
+ equal.reflexive
+ equal.transitive
+ equal.naturality

Total for specification VecSpec
Finished in 20 ms
7 examples, 700 expectations, 0 failure, 0 error

For each law 100 tests with random Vec values are performed and all passed without any error. Now we are a lot more confident that our Monoid[Vec] is actually a monoid.

The complete example with a sbt build file is on GitHub (scalaz-monoid-example). Just clone it and run sbt test to see the law testing in action. :-)