What brought me to Inversion of Control in the first place ..

.. was that it made for very nice decomposed, yet very declarative applications. A year after getting involved with Inversion of Control (IoC), I found myself as Head of Development of a dot-com in London and hired Joe Walnes as one of the engineers. Amongst many things that I found hard to convince the developers of the need for, was IoC. Roll forward a couple of years and we are both at ThoughtWorks in London as consultants. I was still pushing Apache-Avalon at that time (though it was beginning to fall apart politically) and Joe was starting his book (the badly named Java Open Source Programming with his OpenSymphony buddies), we were still not on the same page as each other in respect of IoC. Joe’s epiphany was that that IoC made for components that were easier to unit test. That put us on the same page finally. Well nearly, WebWork (the nascent version of v2 for the book) favored setter injection. It was easier to see how that was better than Avalon’s incredibly verbose Interface Injection, but to me (and Joe’s book reviewer Rachel) Constructors were the way forward (yawn - Aslak and I started PicoContainer soon after).

I’m still there though. Favoring IoC for reasons of clear, decomposed, declarative architecture (as well as unit-test cleanliness).

Spring’s XML never attracted me, but I have to say Guice’s modules do not either. What I really want is a place where I can tersely define my application. I’ve had no love for EJB since 98 because of the ridiculousness of the separations and lookup of entity and session beans, so EAR and WAR solutions are not something I recommend. What I want really to is launch a whole app from the command, being as close to the main() method as possible. IoC of course, with Constructor Injection being the chosen form, and config being pulled from command line args or a properties file at la PicoContainer 2.0’s component configuration .

main {
  component(:key => MyStore, :impl => StoreThatWrapsHibernate)
  webContainer {
    component(:key => Shoppers, :impl => ShoppersImpl)
    context(:url => 'store/cart') {
      component(:key => Cart, :impl => CartImpl)      
      webwork(:suffix => '.do', :pages => '.jspx') {
        action(:url => 'addTo', :impl AddToCartAction)
        action(:url => 'removeFrom' , :impl RemoveFromCartAction)
        action(:url => 'updateQty' , :impl UpdateCartQuantityAction)
      }
    }
    context(:url => '/') {
      staticContent('c:\mywebapp\staticContent')
    }
  }
}

What this as leaves as lasting is a place where new developers to the project can grasp some of the architecture of the app in a since place. The above is actually a Ruby syntax, making it a script language and implicitly not as type-safe as the Guice way (one or more modules in a Java class).

For the unit testing of the actions, you’ll just live in a simple world of Junit and Mockito (or JMock or EasyMock):

@Test
public void addToCartShouldAddToDatabase() {
  Cart mockCart = mock(Cart.class);
  stub(mockCart.addToCart(1,2,3)).toReturn(true);
  // ...  
  AddToCartAction atc = new AddToCartAction(mockCart, 1);  // user = #1 via injection
  atc.setStockItem(2);
  atc.setQuantity(3);
  atc.execute();
  verify(mockCart).clear();                     
  verifyNoMoreInteractions(mockCart);
}

For the unit testing of other pieces also live in that Junit/mock world. For full stack integration testing, you are either doing to the whole the script at the top of the page, after ensuring that you’ve configured a database in a known state. Alternatively, you want to kick off a version of ‘the stack’ for testing over the wire ….

main {
  component(:key => MyStore, :impl => InMemoryStore)
  webContainer {
    component(:key => Shoppers, :impl => ShoppersImpl)
    context(:url => '/') {
      component(:key => Cart, :impl => CartImpl)      
      webwork(:suffix => '.do', :pages => '.xml) {
        action(:url => 'a' , :impl AddToCartAction)
        action(:url => 'r' , :impl RemoveFromCartAction)
        action(:url => 'u' , :impl UpdateCartQuantityAction)
      }
    }
  }
}

… and test it with HTTPUnit, because we’re interested in the interactions more than the display. Maybe we’re load testing, maybe not. How about this for Cart testing:

webContainer {
  context(:url => '/') {
    component(:key => Cart, :impl => StubCart)      
    webwork(:suffix => '.do', :pages => '.xml) {
      action(:url => 'a' , :impl AddToCartAction)
      action(:url => 'r' , :impl RemoveFromCartAction)
      action(:url => 'u' , :impl UpdateCartQuantityAction)
    }
  }
}

Many will suggest that more than one of these scripts would be unwieldy to support. You are likely to have more than one integration test-case, so might be one script for live, and more than script one for testing. There could be a number of strategies for minimizing that. One is subject to taste:

public class SomeTestCase {
  @Before
  public void makeRootContainerAndStubsAndStartServer("myapp.rb") {
    String compositionScript = loadCompositionScript();
    compositionScript = compositionScript.replace("StoreThatWrapsHibernate", "InMemoryStore"); 
    rootContainer = processScript(compositionScript);
    rootContainer.start(); // kick off daemons that listen on sockets, and threads etc.
    inMemoryStore = rootContainer.getComponent(InMemoryStore.class);
    rootContainer.start();
  }
  / etc
}

Another way would be to some how intercept the injection phase, and swap the store implementation programmatically:

public class SomeTestCase {
  @Before
  public void makeRootContainerAndStubsAndStartServer("myapp.rb") {
    String compositionScript = loadCompositionScript();
    rootContainer = processScript(compositionScript, 
        new InjectionInterceptor().exchange(StoreThatWrapsHibernate.class).for(InMemoryStore.class));
    rootContainer.start(); // kick off daemons that listen on sockets, and threads etc.
    inMemoryStore = rootContainer.getComponent(InMemoryStore.class);
    rootContainer.start();
  }
  // etc
}

Now this is more like it. A design that would allow us to strategically swap out components for particular test scenarios. Of course we have not actually coded that last in PicoContainer 2.0 , but I hope to discuss it with the gang. Nor have we delivered on the JRuby grammar beyond some early experiments in NanoContainer.

Guice’s modules do not excite me. It is true that you can arrange them into logical grouping, but when you’re trying to tackle the same testability aspects, you find yourself slowly plodding down an inheritance road to leave it open for overriding of the provision of one or more components for a test case/suite. You may even use some inner class magic to keep that all in the same class file. Neal Gafter’s closures is going to give Guice modules a new lease of life, I guess, but its still not what I want. I want that terse/expressive script that launches an auto-wired decomposed Java application, with Jython, JRuby or Groovy[1] being the compositional language.

fn1. Groovy may be the more Java friendly script style, but they dropped their interpreter (look in page for Interpreter.java) some years ago, giving me concern for other reasons, which I’ll explain another day.



blog comments powered by Disqus

Published

March 22nd, 2008
Reads:

Categories