Wednesday, February 27, 2008

An Assertions-DSL in Scala

As I am currently working on some Java code, I am writing unit tests in JUnit and using the quite helpful Assertions and Matchers library Hamcrest. Hamcrest improves assertions by providing matcher to check, whether a current results matches an expectation. It allows the construction of complex logical statements, that are actually not regular Java expressions, but rather runtime expression trees. A benefit of this matchers over conventional assertions is, that they can provide much better error messages. Visit the Hamcrest project website to get more insights in this helpful and slim framework.

Unfortunately Java restricts the design of matcher libraries by not allowing operator overloading and new infix operators. In Scala it is possible to improve the readability of assertions a lot. The following code shows some assertions in Scala:
import matcher.Assertion._;

object MatcherTest {
def main(args : Array[String]) : Unit = {
val x = 5;
val varX = variable("x", x);
assertThat(varX.==(5));
assertThat((varX == 6).or(varX == 5));
assertThat((varX == 6) or (varX == 7) or (varX == 8));
val s = "test";
val varS = variable("s", s);
assertThat(varS.==("t"));
assertThat((varS == "tz") or (varS == "rd"));
assertThat((varS == "t") or ((varX == 5) and (varX == 8)));
}
}

This is all valid Scala code. In Scala it is allowed to use regular methods as infix operators.

Actually instead of raising an exception assertThat just prints the error message:
Expected that (((x == 6) or (x == 7)) or (x == 8))
Expected that (s == t)
Expected that ((s == tz) or (s == rd))
Expected that ((s == t) or ((x == 5) and (x == 8)))


The following code block is a basic implementation. Every expression subclass implements a method to pretty print itself and a method to evaluate whether the expectation is met.
object Assertion {
def assertThat(expression : Expression) = {
if (!expression.evaluate) {
println("Expected that "+expression.text);
}
}

def variable[T](name : String, x : T) = new Variable[T](name, x)

protected abstract class Expression {
def evaluate : boolean
def text : String = "("+textInternal+")"
def textInternal : String
def or(expression : Expression) = new Or(this, expression);
}

protected class Variable[T](name : String, x : T) {
def ==(y:T) = new Equals(this, y);
def getX = x;
def text = name;
}

protected class Equals[T](v : Variable[T], x : T) extends Expression {
def evaluate = v.getX == x;
def textInternal = v.text + " == " + x;
}

protected class Or(e1 : Expression, e2 : Expression) extends Expression {
def evaluate = e1.evaluate || e2.evaluate;
def textInternal = e1.text + " or " + e2.text;
}
}

There is still a piece of RY (Repeat Yourself) in the code. In fact I had no idea how to avoid the construction of the named variable. Perhaps some Scala gurus can give me a hint.

No comments: