Last time I was writing about few tricks in creation of test fixtures. How to improve their maintainability in the long run and how to use some of the groovy’s DSL fatures to make them easier to use and read. Most of the techniques in the previous article were trying to wrap SUT in the invocations that are focused on the test context itself, without the need to go into every detail of SUT API. This time I want to focus more on enhancing the SUT itself, that will add to the readeability of the test, without compromising the design of the SUT.

Reflections

This might sound like a blasphemy for many, but wait with your wrath and vengeance till the very end. First let’s gather few facts:

  1. Unit Tests are more on the white box than black box side. Sure the more we go into the white box side, the more we make our tests exposed to non-contract breaking changes of the SUT, but we should never forget about that fact
  2. Reflection in general is bad in terms of maintainability and readability, but Groovy language has a lot of features in it, that omit the visibility of fields, methods, constructors etc., so we get some insights into the object inner structure through the means of a language
  3. Most of the TDD practitioners knows that from time to time, when using standard JUnit or TestNG, we were forced to relax something in the SUT, just to be able to test it.

Having all these points in mind, it does not sound so strange to suggest using groovy to get into SUT internals if it saves us from making changes in the SUT, that we would not make for any other reason.

Let’s examine the case bellow:


class Order {
    
    private boolean isPaid;
    private LocalDateTime paidAt;
    
    void handle(OrderPaid event, Deliveries deliveries) {
        if(hasNotBeenPaidYet()) {
            paidAt = event.occuredAt;
            deliveries.deliverOrder(this);
        }
    }
    
    private boolean hasNotBeenPaidYet() {
        return paidAt == null;
    }
}

interface Deliveries {
    
    void deliverOrder(Order order);
}

In the above code, we have some Order that tracks many different things that happen when, most likely a customer wants to order some goods. The important thing is, that the payment is in most cases an asynchronous action and will likely happen in some other system. The only thing that we can do is to react to this event and process it accordingly. Most likely this event will be send over some queue, which means that it can be delivered more thant just once (see at least once delivery). Still, our application is bounded to do manny actions only once, like communicating with the customer, sending order to the delivery etc. We can handle that, by simply checking whether payment has been already processed or not, here achieved by remembering the time when the order has been paid and checking through hasNotBeenPaidYet() method. Without using groovy, the only way to test this interaction would be to make the ‘'’paidAt’’’ visible at least on the package level. That’s not that tragic, but still such information is rarely used by Order clients, so exposing it “just because” doesn’t make much sense. With groovy (and spock) however, we can do it in a bit different way:


   def 'handles order payment' {
      given:
        OrderProcess order = filledOrder()
      and:
        Deliveries deliveries = Mock()
      
      when:
        order.handle(orderPaid(), deliveries)
        
      then:
        1 * deliveries.deliverOrder(order)
      and:
        order.paidAt == orderPaid().occuredAt
   }

Since it’s groovy we can get access to paidAt property of Order object, without changing its visibility from private to package. The same is true for private methods, constructors, static fields and so on. So whenever you feel the need to test something that’s not visible from the test perspective, groovy allows for more. Now when you know this, proceed with caution as most of the time you are either overspecifing the test, making it actually harder to maintain, or missing some important hint about your design, which would allow you to not being in this position in the first place.

Overriding parameters and monkey patching

Closures, default methods and traits are not all of what groovy has to offer in terms of DSL creation. Another powerful feature is the ability to override parameters. Groovy not only allows to override few predefined operators, but overrides many parameters for existing Java classes. Good example of Java class with overriden operations is BigDecimal. In groovy, whenever we create a new number, if not defined explicitly it will be of BigDecimal type. Even though it’s a java class, due to parameters overriding, we can do things as follows:


BigDecimal a = 1.0
BigDecimal b = 2.0

assert a + b == 3.0

For simple cases it is well enough. From time to time however, when we are dealing with a bit more complex domains, there is a possibility that some parts of our model will start to resemble a mathematical Closure, with a defined set of possible values and operations that are closed under its set. In DDD a pattern that resembles deeply such mathematical closures are named Closure of Operations. In most of the cases Closure of Operations is created around a subset of one or more ValueObjects. Let’s take a look at an example of such construction.


class Length {
    
    private final int value;

    private Length(int value) {
        if(value < 0) {
            throw new IllegalArgumentException("Length can't have a negative value: " + value);
        }
        
        this.value = value;
    }
    
    static Length m(int meters) {
        return new Length(meters * 1000); //conversion to millimeters
    }
    
    static Length cm(int centimeters) {
        return new Length(centimeters * 10);
    }
    
    static Length mm(int millimeters) {
        return new Length(millimeters);
    }
    
    Length add(Length length) {
        return new Length(this.value + length.value);
    }
    
    //Subtractions is not part of mathematical Length closure, but can be quite useful either way
    Length substract(Length length) throws IllegalArgumentException {
        return new Length(this.value - length.value);
    }
}

For such class, we can have tests as follows:


import static Length.m
import static Length.cm


class LengthSpecification extends Specification {

    def "adds lenght"() {
        given:
            Length m10 = m(10)
            Length cm13 = cm(13)
        expect:
            m(15).add(m(5)).add(cm(10)).add(cm(3)) == m10.add(m10).add(cm13)
    }
}

Although it’s readable for any Java developer all these nested methods in a chain looks much worse than a simple + operations that we can do using int or BigDecimal. Luckily Groovy allows for parameter overriding, and in this scenario, we could simply override parameter + to bring back this look and feel of a Number. In order to do so, we just need to take advantage of groovy operators overriding and implement plus method on an given object:


class Length {
    
   //All the methods from the previous example 
    
   public Length plus(Length other) {
       return this.add(other);
   }     
}

and then simply:

import static Length.m
import static Length.cm

class LengthSpecification extends Specification {

     def "adds length"() {
        given:
            Length m20 = m 20
            Length cm13 = cm 13
        expect:
            m(10) + m(10) + cm(3) + cm(10) == m20 + cm13
     }
}

It looks much better right? We can read it almost as if we were making notes. The only problem is that to do so, we need to modify the SUT in order to implement plus method. Well, here is where the technique called monkey patching comes in. We can actually add methods to existing types and make these methods available in the whole source-set. Here is how to do it:

  1. First we need to define the plus method for the Length class, without changing the class itself
package com.szymonhoma

class LengthExtensions {
    
    public static Lenght plus(Lenght self, Lenght next) {
        return self.add(next)
    }
}
  1. Now when we have the extension, we need to define it as ExtensionModule. In order to do so, we need to provide an additional file in META-INF folder, located under src/test/resources/META-INF/services/ and call it org.codehaus.groovy.runtime.ExtensionModule. We create it under src/test path so that it won’t leak to production code. It takes structure as follows:
moduleName=local test extensions
moduleVersion=0.0.1 
extensionClasses=com.szymonhoma.LengthExtensions,com.szymonhoma.SomeOtherExtensionIfRequired

After that we can start using + operator inside our tests, without even touching the Length class. The more in detail description of how to use ExtensionModule can be found at mrhaki.blogspot.com

Coercion and monkey patching

Let’s stay a bit more with our Length class here. As a vigilant reader might have noticed, I’m allowing myself in the examples above to use such notation Length m20 = m 20, that ommits (). This is simply another way of methods invocation provided by groovy and it allows me to have a bit more fluent experience when reading such tests. Let’s explore some other ways of creating objects in groovy that are reader friendly. It might seem that the most straight forward way of creating a Lenght would be to do it as follows:

Length a = 10

Looks ok? Well actually, one might ask whether we’ve just created a 10 cm, m or mm Length. That’s a fair question in general, however in this case we are dealing with units and the basic unit of length in SI is 1 Meter, so I would allow such construction. In addition, we might want to be able to express cm and mm as simply 0.01 and 0.001, which would allow us to write:

Length m1 = 1
Length cm1 = 0.01
Length mm1 = 0.001

Looks even better, right? Well maybe not quite, as one might want to ask “But what about 0.00000001 Length, will we allow such constructions to occur?”. Obviously not, as our model states precisely that we can have at most 1 mm Length and nothing smaller. Let’s write some specification then:

def 'allows to create Length in reader friendly manner'() {
    given:
        Length m1 = 1
        Length cm1 = 0.01
        Length mm1 = 0.001
        
    expect:
        m1 == m 1
        cm1 == cm 1
        mm1 == mm 1    
}

def 'does not allow to create values of precision beyond our skills'() {
    when:
        Length um = 0.000001
        
    then:
        thrown IllegalArgumentException
}

How to achieve such effect? Well it’s not possible in such form, but let’s see how close can we approach it. In groovy there is a mechanism known as coercion, which allows nicer types conversion. It can be invoked through the use of as key-word, so we might write in groovy: Integer a = 1.00 as Integer which converts new BigDecimal(1.00) to Integer or Set values = [1, 2, 3] as Set which converts a List to a Set. The tricky part here is that we want to achieve the conversion from BigDecimal to Length and BigDecimal already has some conversions defined. Here is how to deal with that.

Let’s create a BigDecimalExtension class with public static def asType(BigDecimal self, Class target) method defined (it’s used to support the coercion mechanism)

class BigDecimalExtension {
    
    static def asType(BigDecimal self, Class target) {
        switch (target) {
            case Length:
                if(self.precision() > 3) {
                    throw new IllegalArgumentException("can't produce length of greater precision that mm")
                }
                return Length.mm(
                        self.multiply(1000).toInteger()
                )
            default:
                return DefaultGroovyMethods.asType(self, target)
        }       
    }
}

Few notes to this implementation:

  • we are extending BigDecimal, so our Length case is one of the possibilities (hence switch) and that’s why I wouldn’t define this coercion f.e. in LenghtExtension class
  • We’ve said that we won’t allow for greater precision that millimeters, so we need to check this before creating Length
  • In order to not break any other coercions that might have been defined in groovy sdk, we need to use DefaultGroovyMethods.asType(self, target). Important thing here is that we are using internal Groovy api at this moment, and as it’s not likely to be changed, there is no guarantee for it in the long run. Let me cite the docs:
    NOTE: While this class contains many 'public' static methods, 
    it is primarily regarded as an internal class (its internal package name suggests this also). 
    We value backwards compatibility of these methods when used within Groovy 
    but value less backwards compatibility at the Java method call level. 
    I.e. future versions of Groovy may remove or move a method call in this file 
    but would normally aim to keep the method available from within Groovy.
    

    You’ve been warned.

Next, we need to add this extension to our extension module, exactly as in the case of LengthExtension described previously. When all of it is defined, we can use our coercion in tests as follows:

def 'allows to create Length in reader friendly manner'() {
    given:
        Length m1 = 1 as Length
        Length cm1 = 0.01 as Length
        Length mm1 = 0.001 as Length
        
    expect:
        m1 == m 1
        cm1 == cm 1
        mm1 == mm 1    
}

def 'does not allow to create values of precision beyond our skills'() {
    when:
        Length um = 0.000001 as Length
        
    then:
        thrown IllegalArgumentException
}

def 'can be used as method arguments as well'() {
    when:
        Length a = 5.00 as Length

    expect:
        a.plus(10.00 as Length) == 15.00 as Length
}

Unfortunately we can’t have something like a + 10.00 as Length, due the way in which the parameters are resolved. I personaly like this formalization of as Length as it makes quite good job when I read such tests, but it’s for the reader of this article to decide, whether it’s worth the hassle. Is there really nothing more that we can do? Well there is one small thing. Groovy supports a coercion from [] to any type, assuming that next items in the list are the constructor arguments of a type that we want to create. It might look something as follows: Length a = [10.00]. Let’s write some test for it:

def "allows for creation by [int] coercion"() {
    given:
        Length a = [1]
    expect:
        a == Length.mm(1) //will work
}

def "allows for creation by [BigDecimall] coercion"() {
    given:
        Length a = [1.11] //will fail
    expect:
        a == m(1) + cm(11)
}

The first test works, because we allready have a constructor in Length class that takes single int parameter in and Groovy doesn’t really care whether it’s private or not. For the second example however we need to have a proper constructor defined. To do so, we simply need to add a private constructor to the Length class itself and just pretend that it has allways been there.

class Length {

    private final int value;

    //Just for test usage
    private Length(BigDecimal val) {
        ...
    }
    
    private Length(int value) {
        ...
    }
}

def "allows for creation by [BigDecimall] coercion"() {
    given:
        Length a = [1.11] //now will work like a charm
    expect:
        a == m(1) + cm(11)
}

That’s a good trade-off for me, as it’s very simple and the constructor is not visible for the Java code either way. It’s worth to mention that Groovy allows adding non existing constructors through metaClass, but it has a lot of issues, like high probability of receivingStackOverflowException.

Ok so that’s it.

I hope that someone might find this (and the previous article) helpful. When you are looking for more ways of improving your tests I recommend you this recording of Jakub Nabradlik’s Improving your Test Driven Development in 45 minutes talk at GeeCON 2018 and on few other occasions.