Thursday, February 17, 2005

Defaulted Pluggable Configuration Providers

I still get comments and questions about my XmlSerializerSectionHandler, even though I wrote it two years ago (hey, two years ago this week, in fact!). People seem to love it. What's funny is that I never use it any more. I realized a long time ago that using XmlSerializer directly to slurp a file into an object is only one or two more lines of code, and has the advantage of not making me store information in the program installation directory: a no-no for many reasons. Instead, I can put it wherever it belongs


When I started doing Test Driven Development, doing things this way presented me with the same conundrum that many face: do I test configuration by writing data to the current user's profile directory (potentially destroying whatever application settings they had stored there), or is there some other way to do it? (This is a variation of  “Should I test against the database?”) Well, after having answered that question a few hundred times, I settled on this pattern. I know I'm not the first (or even the thousandth) person to settle into it, but I've found it useful, and hope someone else out there will too.


The basic idea is to make the thing that gets the configuration pluggable. Then you can have the real one that reads from the user profile directory, and a mock one that returns whatever test data you like. I usually also couple it with the idea of making the one that pulls from the real location the default, so that you can use the class in normal scenarios without having to do anything special. Something like this:


public interface IGetConfigData {
  ConfigData GetConfigData();
}


public DefaultConfigGetter : IGetConfigData {
  public ConfigData GetConfigData() {
    // Gets "real" config data
  }
}


public class MyClass {
  private IGetConfigData _configGetter = new DefaultConfigGetter();


  public IGetConfigData ConfigGetter {
    get { return _configGetter; }
    set { _configGetter = value: }
  }
}


public MockConfigGetter : IGetConfigData {
  private ConfigData _data;


  public MockConfigGetter(ConfigData data) {
    _data = data;
  }


  public ConfigData GetConfigData() {
    return _data;
  }
}


[TestFixture]
public class MyClassTests {
  private MyClass _mc;


  [SetUp]
  public void SetUp() {
    _mc = new MyClass();
    _mc.ConfigGetter = new MockConfigGetter(GenerateTestConfig());
 
}


  private ConfigData GenerateTestConfig() {
    // Whip up test data and return it
  }

}

4 comments:

  1. Craig, one question is why do you explicitly implement the mock version? Why not use the NUnit's built in mock objects support and set use DynamicMock.ExpectAndReturn() if verifying how consumers of IGetConfigData interact with the interface? Still, the pluggable aspect of interface dependencies is worth mentioning to readers.

    ReplyDelete
  2. Well, the main reason is, I'm not familiar with it. :)



    But isn't it a feature of NUnit 2.2? I'm still using 2.1.4 in most places.

    ReplyDelete
  3. That makes sense then! :) This cuts down the code for test fixtures which always helps new comers get over the resistance to TDD I think. This is the pseudo-code:



    // NMock will generate a type that

    // implements the interface

    DynamicMock mockConfig =

    new DynamicMock( typeof(IGetConfigData));



    // pass in a sample of config data - if this

    // method isn't called the test will fail

    mockConfig.ExpectAndReturn("GetConfigData",

    new ConfigData());

    return (IGetConfigData)mockConfig.MockInstance;

    ReplyDelete
  4. OK, that looks moderately useful. Not *much* less code, but a bit.



    I'd probably stay away from this for anything more complicated than this simple example, though - to me it seems less readable, and since it's only slightly less verbose, the tradeoff doesn't seem worth it for more substantial situations.



    But I'm willing to admit I might be wrong. :)

    ReplyDelete