Home / StructureMap – Software Design Concepts / StructureMap – Inversion of Control

StructureMap – Inversion of Control

Inversion of Control (IoC) is an essential tool for any software designer’s design toolbox because it’s often a great way to break dependencies between classes and promote loose coupling.  IoC is vital for doing Test Driven Development without pulling out all of your hair in frustration.

Next Prev

Real Life Example

 I’m doing an interesting refactoring early this week on some existing code.  I apologize in advance for being a little vague, but the NDA thingy gets in the way a bit here.  The product that my team works on is basically a B2B hub between sender companies (spokes) and receiving companies.

 

The application:

  1. Receives a message in one of a handful of industry standards and 3rd party formats
  2. Converts the data into a canonical data format
  3. Performs a variety of business validations
  4. Translates the results into yet another industry standard for transmittal to another downstream system

The translation code is obviously critical to the system, so it’s very important that the automated testing is rock solid.  Moreover, that code would be extremely useful in many other contexts throughout our enterprise.  The code works just fine and there is a robust suite of automated tests ensuring that the code continues to work.  I had tried in the past to adapt this code inside of an automated regression test tool without any success because it had too much “tail.”  What I mean by this is that the code can only function with some specific dependencies on a couple of different data sources that weren’t necessarily easy or even possible to recreate without bringing in a large fraction of the system.  Using the translation code requires some data access code which in turn requires some configuration code and external settings in a Registry (We’re going to make this go away too.  I detest using the Registry for any configuration, much less as a Plugin strategy.).

In an ongoing effort to speed up a slow (15-20 minutes) build, I managed to break the integration tests for the translation classes and had an opportunity to look much closer at this code.  Here’s roughly what I found:

public class Translator1
{
private readonly IDataSource _source;
public Translator1(IDataSource source)
{
_source = source;
}
public Translator1()
{
_source = new CompanyDataSource();
}
public XmlDocument TranslateFlatFile(byte[] contents, string senderId, string receiverId)
{
XmlDocument document = this.startNewXmlDocument();
CompanyInfo sender = _source.FetchCompanyInfo(senderId);
CompanyInfo receiver = _source.FetchCompanyInfo(receiverId);
this.createSenderNode(sender, document);
this.createReceiverNode(receiver, document);
// Do a lot of other stuff
return document;
}
private void deepInTheBowelsOfTheClass(string key)
{
string something = DataSourceSingleton.Instance.LookupValue(key);
// do something with "something"
}
[OTHER METHODS]
}

And the test fixture looks a bit like this:

[TestFixture]
public class Translator1TestFixture
{
[Test]
public void WriteTheSenderNode()
{
// The sender and receiver data is assumed to be in the database -- somewhere
Translator1 translator = new Translator1();
byte[] contents = this.readFlatFile();
XmlDocument document = translator.TranslateFlatFile(contents, "ID00001", "ID00210");
XmlNode senderNode = document.DocumentElement.SelectSingleNode("Sender");
Assert.AreEqual("Austin", senderNode["City"].InnerText);
}
private byte[] readFlatFile()…
}

 

So what’s so terribly wrong with this code?  From my standpoint there are several problems.

 

  • Setting up the tests.  The unit tests cannot function today without known data being previously setup in the database external to the TestFixture.  When you do write tests against a live database you’re forced into a “least evil” decision.  One of the things I talked about in an earlier post was the desire for tests to reveal the intention of the code.  One of the best ways to do this is to make sure that the test is largely self-contained.  In this case the data assumed to be inserted into the database prior to the test running.  One way we can make this test self-contained is to do the database data setup inside the TestFixture class itself.  That’s okay with small chunks of data, but it doesn’t scale very well.
  • The tests are not isolated.  The tests break if other tests overwrite the data in the database.  Life is happier by far when each test is truly isolated and order-independent.  The dependency on the singleton in the Translator1 class certainly doesn’t help either.  I’m fighting with a set of tests today that are not isolated.  They exhibit some interesting behavior like running successfully one at a time but failing occasionally when they’re run with other tests.  I’m not happy.
  • Debugging the unit tests.  Look at the WriteTheSenderNode test method.  There is an assertion that checks that the “City” node has a value of “Austin.”  Just looking at the test code you won’t be able to see where this value comes from.  It’s not clear from the test code why the value of the xml node is supposed to be “Austin.”  You can partially beat this problem with comments in the assertions, but that’s not enough in my book.  Comments are often a crutch for obfuscated code and this isn’t an exception.  Another thing to keep in mind is that granular tests are easier to debug than coarse tests.  This code today only has integrated tests without any unit tests.
  • Test execution time.  Tests that run against the database are slower than tests that run completely inside one process.  I’m writing this post while I wait for some very slow integrated tests to run.  Slow tests are a form of inefficiency.  I really want to test the translation code here, not the data access.  If I can isolate the translation functionality away from the database structure, I can make the tests faster.
  • Reuse the code.  As I’ve stated before, it would be very advantageous to reuse this translation code.  Today this is difficult because of the tight coupling to other classes.  The dependency on the specific data access class is easily remedied by a little “constructor injection,” but there is still the dependency on the singleton buried deep inside the class.  To use the translation code I have to bring with me everything that damn singleton needs too.  Moreover, it’s not going to be obvious to someone else attempting to use the translation code what they need without tracing deep into the translation to figure out the external dependencies of the singleton.  Singleton == testing pain.  I haven’t done enough research into the singleton usage, but one way or another we’ll remove the dependency on the singleton.

Refactoring to Inversion of Control

My thought on the translation code is to apply Inversion of Control to the translation code to remove the database dependency.  Let some other class be responsible for running out and fetching the receiver and sender information and “push” it into the translator.  I could have abstracted the data source class as an interface and used Constructor Injection to push alternative classes or mocks into the Translator2 class.  My thinking in this case is to simplify both the reuse of the translation code and eliminate the mocking overhead (~70 automated tests) by eliminating the interaction with a data source class altogether.

The code below is a skeleton of the approach I’m going to take to refactor our translation code (someday).  Here’s how I think the new code stacks up to the old code.

  • Setting up the tests.  The input data is created within the test itself because the inputs are just objects.  Don’t ever discount the value of Intellisense in creating test data.  Working with SQL statements or DataSet’s is laborious and error prone.
  • The tests are isolated.  The translation code no longer depends on any kind of external or shared data inside the tests.
  • Debugging.  The tests are somewhat easier to debug because the tests are self-contained.  Debugging one of these tests if they break is simpler because you don’t have to keep “ALT-Tabbing” between the database window and the code.  I’d also argue that the test code is much more intention revealing.
  • Test execution time.  These tests will run faster because the external network hops to the database are eliminated.  Don’t underestimate the importance of fast tests.
  • Code reuse.  I can now reuse the translation code more easily in other contexts.  I don’t have to have any special database structure or external configuration anymore to use the translation code.
public class Translator2
{
public Translator2()
{
}
public XmlDocument TranslateFlatFile(byte[] contents, CompanyInfo sender, CompanyInfo receiver )
{
XmlDocument document = this.startNewXmlDocument();
this.createSenderNode(sender, document);
this.createReceiverNode(receiver, document);
// Do a lot of other stuff
return document;
}
#region other methods…
}
[TestFixture]
public class Translator2TestFixture
{
[Test]
public void WriteTheSenderNode()
{
CompanyInfo sender = new CompanyInfo();
sender.City = "Austin";
// set other sender properties
CompanyInfo receiver = new CompanyInfo();
// set receiver properties
byte[] contents = this.readFlatFile();
Translator2 translator = new Translator2();
XmlDocument document = translator.TranslateFlatFile(contents, sender, receiver);
XmlNode senderNode = document.DocumentElement.SelectSingleNode("Sender");
Assert.AreEqual(sender.City, senderNode["City"].InnerText);
}
private byte[] readFlatFile()…
}

All I’ve really done is move the responsibility for fetching data out of the database into another class.  It’s a small change but it had a positive impact on both the code and the testing.  If you think about it, you could actually say that software design boils down to “Where should this code go?”  I think there’s a watershed moment in every developer’s career when they start look more at their code as a structure and less as a bag of statements.

Other Examples

 We’re taught that the key to safe lifting is “push, don’t pull.”  I like to think of Inversion of Control as applying this mantra to code.  IoC is fairly common, and much more so with Test Driven Development practices.  Here are some other examples of IoC you might run into.

  • The Data Mapper pattern is the central concept in many Object/Relational Mapping tools.  Using an external mapper class to handle persistence duties for a business domain class has several advantages.  The domain classes can become simpler because they’re no longer interacting with any kind of data class.  You can also insulate the business domain from the database structure and vice versa.  Testing the business logic can be much simpler with a domain model.  My company’s main application today relies on far too many T-SQL stored procedures.  Some of our multi-billion dollar clients seem to think they have the right to run our application on Oracle instead.  We’re eventually going to arrange our business logic along domain model principles and take advantage of an O/R mapping tool for multi-database support.
  • Model View Presenter (MVP) might not be a true example of IoC, but it’s similar in philosophy.  When you use the MVP you make the “view” classes completely passive.  Instead of the view making requests to the controller, the controller tells the view what to do.  MVP is the best mechanism that I know for doing TDD with user interface clients.
  • A side effect of using Dependency Injection for configuration is that your classes are no longer dependent on any kind of external configuration file (or God forbid, the registry).  Here’s a simple example of “push” configuration.  The PushClass will be constructed by some kind of Builder class that reads the configuration and returns a PushClass object.  I’m a huge fan of this technique after a couple of projects now.
public class PullClass
{
private int _threshold;
public PullClass()
{
_threshold = int.Parse(System.Configuration.ConfigurationSettings.AppSettings["Threshold"]);
}
}
public class PushClass
{
private readonly int _threshold;
public PushClass(int threshold)
{
_threshold = threshold;
}
}

 

  • If you look at the example user story from my article on Assigning Responsibilities, I used IoC to isolate the functionality for creating HTML from an Invoice so that this functionality could be tested without a lot of interaction with other classes.
Next Prev