+1 (669) 231-3838 or +1 (800) 930-5144

It has been a few years since I’d done more than review or read Java code. I’ve been using Java on and off over the years but my last few years have been spent writing UI/JavaScript in browsers and Node.js (along with Python, Perl, Groovy, Bash scripting…but no Java). So as I approached the task of tackling an ODL ‘application’ for the first time I doubted my previous Java stint with GWT years ago were going to pay off…

TDD and DI

The task was to create a new set of functional components which collaborated with the code layer immediately responsible for communicating with MDSAL and other ODL features. For this I used Spock and a TDD (test driven development) approach to write tests which exemplify the required functionality based on a hastily sketched interface and the acceptance criteria in the JIRA item. Typically all that is needed for implementation, especially when using dependency injection friendly patterns, are the bits that wire the components together – variables to hold references and functional methods with no implementation but what will satisfy the compiler. The simplified lenient Mock and Stub capabilities from Spock and the use of Java interfaces substitutes whatever results of the functions are needed. Once a level of satisfaction that the behaviors needed and the inter-relationship between components is complete the set of unit tests become the acceptance criteria of the class implementations.

Constructor Visibility

To be friendly with unit testing practices and the use of DI (Blueprint in ODL), the visibility modifiers of Java are used to define at least two constructors: a default visibility (“package-private”) constructor and a public constructor. Unit tests reside in the same package as the ‘subject under test’ and access the default visible constructor for a full set of dependency injection parameters. This includes things as the Logger and all components which it collaborates. The public constructor only exposes a subset of the dependency injection parameters in the default visible constructor. This limits the exposure in DI to just the desired injection properties (e.g. Logger is omitted since that flexibility is unnecessary for normal use).

DI Constructor Pattern


package com.example.java;

import com.google.common.base.Preconditions;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

public class ServiceImpl implements Service {
  private Logger logger;

  public ServiceImpl() {
    this(LoggerFactory.getLogger(Service.class));
  }

  ServiceImpl(Logger logger) {
    Preconditions.checkNotNull(logger);
    this.logger = logger;
  }

  @Override
  public void op() {}
}

The test starts simply as:
Test Specification


package com.example.java

import spock.lang.Specification
import org.slf4j.Logger

class ServiceImplSpec extends Specification {
  def logger = Mock(Logger)

  def "construction succeeds"() {
    when:
      new ServiceImpl(logger)

    then:
      notThrown Exception
  }

  def "construction fails"() {
    when:
      new ServiceImpl(null)

    then:
      thrown NullPointException
  }

  def "op called successfully"() {
    given:
      def sut = new ServiceImpl(logger)

    when:
      sut.op()

    then:
      notThrown Exception
  }
}

Along with a simple regression test provided and the beginnings of the unit test for the class there are additional architectural benefits to this code approach:

  • the default visible constructor is DRY with regards to constructor parameter checking and construction code
  • the public constructors are only responsible for construct default injection properties for the class
  • increasing public access to injection properties, or altering the defaults, can be done with new public constructors if necessary to maintain backwards compatibility

In Action

One of the last challenges to this task turned out to be an integration issue. A component I was ‘hiding’ behind the public constructor actually required a reference to an instance found through Blueprint. This required adding a DI parameter to some public constructors to inject the needed Blueprint reference into the component system. Since the default visible constructors already provided the dependency injection properties, unit tests and implementation remained unchanged in all other aspects and the system functioned as desired.

Pin It on Pinterest