Groovy Inversion Of Control & Unit Testing (IOC)

By adding IOC all objects are instantiated by Dependency Injection i.e. they are injected into each constructor by the IOC factory. So we can switch multiple implementations in without changing code.
Formally: the code calling an object is not responsible for instantiating the object, so you would never use

def blah = new Blah()


1. First you need to install the Spring framework using Maven (or manually)
2. Next, add all objects required in a class to the constructor

So say we have the following interface/class (I have added the two objects to the constructor)

public interface IToolBox {
boolean SaveQcFlag(String limsId, String qcValue)
}

public class ToolBox implements IToolBox
{
private IClarity _clarityCredentials
private IGlsRestApiUtils _glsRestApiUtils
private IDataSourceUtils _dataSourceUtils

public ToolBox(IClarity clarityCredentials, IGlsRestApiUtils glsRestApiUtils, IDataSourceUtils dataSourceUtils){
_clarityCredentials = clarityCredentials
_glsRestApiUtils = glsRestApiUtils
_dataSourceUtils = dataSourceUtils
}
//would of been clarityCredentials = new ClarityCredentials() Not any more!

public boolean SaveQcFlag(String limsId, String qcValue){
//to some stuff i.e save to database
_dataSourceUtils.Save(limsId, qcValue)
}
}

public class DoSomeStuffUsingToolBox implements IDoSomeStuffUsingToolBox{
private IToolBox _toolBox
public DoSomeStuffUsingToolBox(IToolBox toolbox){
_toolBox = toolBox
}
public boolean UpdateQC(int someValues){
//do some logical stuff
def result = (someValues * 12) > 10
_toolBox.SaveQcFlag(123, result)
return result
}
}

Note: The interfaces are important for switching implementations and unit testing (as you will see below)

3. We need to tell IOC how to instantiate for us, and we do so for each object using using the spring.xml file:

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.springframework.org/schema/beans/
http:"www.springframework.org/schema/beans/spring-beans-4.1.xsd">

<bean id="toolBox" class="org.petermac.clarity.tools.ToolBox">
<constructor-arg ref="clarityCredentials" />
<constructor-arg ref="glsRestApiUtils"/>
<constructor-arg ref="dataSourceUtils"/>
</bean>
<bean id="doSomeStuffUsingToolBox" class="org.petermac.clarity.tools.DoSomeStuffUsingToolBox">
<constructor-arg ref="toolBox" />
</bean>
<bean id="glsRestApiUtils" class="org.petermac.clarity.clarityApi.GlsRestApiUtils" />
<bean id="clarityCredentials" class="org.petermac.clarity.Clarity" />
<bean id="dataSourceUtils" class="org.petermac.clarity.DataSourceUtils"/>
Note: On some servers the jar cannot get to the internet to access the schema file so you need to download it and place it next to the ioc file. Then switch the line http://www.springframework.org/schema/beans/spring-beans-4.1.xsd with just spring-beans-4.1.xsd
static void main(String[] args){
IocFactory.Build(clarityCredentials)
def instance = (IDoSomeStuffUsingToolBox)
IocFactory.GetInstance("doSomeStuffUsingToolBox")
instance.UpdateQc(1.2)
}
//if you wanted to assign some settings from
// args or input (say a config file) just register
// it with the IOC container.
public class IocFactory {
public static ApplicationContext _context = newClassPathXmlApplicationContext("Spring-IOC.xml")
public static void Build(IClarity clarityCredentials){
_context.getBeanFactory().removeBeanDefinition("clarityCredentials")
_context.getBeanFactory().registerSingleton("clarityCredentials", clarityCredentials)
}

public static Object GetInstance(String className){
return _context.getBean(className)
}
}

UPDATE: to use IOC with the grails architecture add a conf/spring/resources.groovy

sampleDetailsEngine(SampleDetailsEngine) {
toolBox = ref("toolBox")
}
toolBox(ToolBox) {}

and then provide public properties and set methods in your classes (i.e SampleDetailsEngine)

public IToolBox toolBox
public void setToolBox(IToolBox toolBox) {
this.toolBox = toolBox
}

In the controller just define the property and spring seems to autowire it up

class SampleFinderController {
def sampleDetailsEngine
}

Done………. But why?
Testability. Consider a unit test of UpdateQC -so we would want to mock the Save to database call. We are only testing UpdateQC not save etc as it is a unit test

public boolean UpdateQC(int someValues) {
    //do some logical stuff
    def result = (someValues * 12) > 10
    _toolBox.SaveQcFlag(123, result)
    //mock this (below)
    return result
}

public class MockToolBox implements IToolBox {
    @Override
    boolean SaveQcFlag(String limsId, String qcValue) {
    //do nothing or write to file or something
        return true //or false whatever you need for the test
    }
}

//the test
void UpdateQcTest(){
    def mockToolBox = new MockToolBox() 
    //see below
    def doQcStuff = new DoSomeStuffUsingToolBox(mockToolBox) 
    //inject mocked instance of ToolBox
    assertTrue(doQcStuff.UpdateQC(1.2))
}  

So we can test UpdateQC without calling to the database. Here we are doing our own mocking but you can use some framework such as Moq or Jmockit to do it for you
i.e it will create the mock object than you can set return value based on certain inputs like: doQcStuff.UpdateQC.When(arg == 1.2).Returns(true)

Configuration: Consider having a class that sends sms’s. You may want to have multiple phone companies or test mode (write to file) etc.

public interface ISmsSender {
void Send(String msg)
}

public class SmsSenderTelstra implements ISmsSender {
public void Send(String msg) {
//send using telstra service
}
}
public class SmsSenderTestMode implements ISmsSender {
public void Send(String msg){
//write to file
}
}

So you can switch in whatever implementation you want at run-time – through code or for deployment – by editing the xml file
Speed: If done properly all instantiations will be done only once
Good for coding: When writing new code you only need to add whatever classes you need to your constructor and that’s it. No stuffing around instantiating and figuring out what other classes it depends on etc.

1 thought on “Groovy Inversion Of Control & Unit Testing (IOC)”

Leave a comment