When reading “Elegant Objects” materials about testing, I was always asking myself: why the tests in Java should look so procedural? Usually, class of a typical JUnitbased test suite is nothing but a bunch of procedures insode a class, one per test case. I am not criticizing this style, but lets try to make the tests in elegant way and see what profit it may give to us?
Let’s consider an example:
interface Fraction {
int numerator();
int denominator();
}
A small highlysegregated interface, representing a simple fraction. One can implement it in several ways… oh wait! I am repeating myself. Lets just take some implementations from here:
class FracStatic implements Fraction {
private final int numerator;
private final int denominator;
public FracStatic(int numerator, int denominator) {
this.numerator = numerator;
this.denominator = denominator;
}
public final int numerator() {
return numerator;
}
public final int denominator() {
return denominator;
}
}
class FracSum implements Fraction {
private final Fraction left;
private final Fraction right;
public FracSum(Fraction left, Fraction right) {
this.left = left;
this.right = right;
}
public int numerator() {
final int a = left.numerator() * right.denominator();
final int b = right.numerator() * left.denominator();
return a + b;
}
public int denominator() {
return left.denominator() * right.denominator();
}
}
class FracFromString implements Fraction {
private final String str;
public FracFromString(String str) {
this.str = str;
}
public final int numerator() {
return Integer.parseInt(str.split("/")[0]);
}
public final int denominator() {
return Integer.parseInt(str.split("/")[1]);
}
}
Theoretically, I could make tests suite for each of the class above like this:
class FractionTest {
@Test
public void testSumOfTwoFractions() {
final Fraction sum = new FracSum(
new FracStatic(1, 2),
new FracStatic(2, 3)
);
assertEquals(5, sum.numerator());
assertEquals(6, sum.denominator());
}
@Test
public void testFractionFromString() {
final Fraction fraction = new FracFromString("1/2");
assertEquals(1, sum.numerator());
assertEquals(2, sum.denominator());
}
// etc...
}
Practically, I am already repeating myself. For each case I am repeating assertions on numerator and denominator. Also, two assertions per test is not good enough: one statement test would be better.
And these were simple examples: what if I take FracFromFile
or implement
an SQLspeaking fraction?
I’d need to prepare test environment somewhere: create a file for test or bootstrap test database.
What’s the alternative?
Lets start with eliminating the repeating part of the test cases above.
First, let’s introduce an Assertion
interface:
interface Assertion {
void check() throws Exception;
}
Then, lets encapsulate repeating assertions to its implementation:
class AssertFractionHasCertainNumenatorAndDenominator implements Assertion {
private final Fraction fraction;
private int expectedNumerator;
private int expectedDenominator;
public AssertFractionHasCertainNumenatorAndDenominator(Fraction fraction) {
this.fraction = fraction;
}
public void check() throws Exception {
assertEquals(expectedNumerator, fraction.numerator());
assertEquals(expectedDenominator, fraction.denominator());
}
}
And at last, rewrite the test suite:
class FractionTest extends TestsSuite {
public FractionTest() {
super(
new TestCase(
"assert that 1/2 + 1/3 = 5/6",
new AssertFractionHasCertainNumenatorAndDenominator(
new FracSum(
new FracStatic(1, 2),
new FracStatic(2, 3)
),
5, 6
)
),
new TestCase(
"assert that \"1/2\" string is parsed to a 1/2 fraction",
new AssertFractionHasCertainNumenatorAndDenominator(
new FracFromString("1/2"),
1, 2
)
)
);
}
// etc...
}
In the example above, TestCase
is a certain test case with a single assertion inside, and TestSuite
is a class
which executes a sequence of test cases.
What’s the profit?
It all may look meaningless: what is the reason to outline just a couple of assertions to a separate class? Isn’t it just overcomplicating simple concepts?
I see the following benefits:

Once implemented,
AssertFractionHasCertainNumenatorAndDenominator
assertion can be used on any possible subtype ofFraction
. 
For any highlysegregated interface with clear domainrelated semantics (like
Fraction
) the number of needed assertions is usually limited. For fraction, singleAssertFractionHasCertainNumenatorAndDenominator
would cover the majority of possible test scenarios with fractions. When we test a newFraction
implementation, first thing we would want to test is that fraction under test has certain numerator and denominator. 
AssertFractionHasCertainNumenatorAndDenominator
assertion, like any otherAssertion
implementations, may be released together with theFraction
interface, so that developers who extend the solution by providing newFraction
implementations may reuse its assertions for testing their own stuff. 
Assertion
implementations may be covered with tests like an ordinary objects. Usually,AssertAssertionPasses
andAssertAssertionFails
are enough to cover all possible reusable assertions in the world. 
Assertion
’s may be decorated. This is usually useful for scenarios where certain preconditions must be ensured before executing original assertion. 
Since assertions may be released together with interfaces and covered with tests, it’s not a problem anymore to put a complex logic inside assertion or assertion decorator. Wanna bootstrap a test database to cover an SQLspeaking object? Why don’t make it inside the assertion decorator?

And finally, no matter how complex is assertion, for the
TestCase
and developer maintaining it, this assertion always remains a single statement.
What’s the pitfalls?
So far it sounds too good to be true. But there are several concerns which must be taken into account when using this approach:
 The interfaces of objects under test must be highlysegregated.
 The classes under test must be singleresponsible.
 The public contract of the objects under test must be stable.
The reason is the number of invariants of the stuff under test. In case of highlysegregated Fraction
it’s
simple—we expect it to have certain values for numerator and denominator and that’s enough for 90% cases. For each
such invariant one may write an assertion and use it on each future implementation.
In case of large objects with dozens of methods, like DAO, controllers and services, the number of such invariants is
scaled with each additional method—there will be too many different Assertion
implementations per one object. Also,
since the public contract of such objects is inflexible, these assertions will not be reused effectively. Testing
such objects with reusable assertions is waste.
Elegant objects, on the other hand, if designed right, usually suit the requirements enumerated above. So, write objects in Elegant way, and test them with elegant reusable assertions.
OOTests
The project OOTests provides starter capabilities for writing
and testing with reusable assertions. TestsSuite
, TestCase
and Assertion
in the example above are all
taken from there. Also, it provides a number of common purpose assertions (like AssertEquals
), and
AssertAssertionPassed
/AssertAssertionFailed
, mentioned in the post. Based on JUnit 5.