CodeBetter.Com
CodeBetter.Com
RSS 2.0 via Feedburner
           Do you Twitter? Follow us @CodeBetter

Karl Seguin

.NET From Ottawa, Ontario

TDD Lesson 3 - Teasing the data layer...

Sooner or later during your TDD experimentation, you'll start to creep up against your data layer. I wouldn't be surprised if a lot of new TDD'ers find themselves brushing up against their database sooner than they ought to. I know I did, but try and put it off as I might, I knew it was something I had to start tackling.

Basically, all I want is to be able to pull out all Indicators from my store. Something like a static Indicator.GetAllIndicators() would do nicely (I realize some would prefer to have that off in it's own class). So I write my first test:

[Test]
public void GetAllIndicatorsDoesntReturnNullWhenEmpty()
{
   Assert.IsNotNull(Indicator.GetAllIndicators());
}


My build breaks. I fix it by adding the static method to my Indicator class:

public static List<Indicator> GetAllIndicators()
{
  return null;
}


My test is breaking, and the easiest way to make it pass is to simply do:

return new List<Indicator>();

The above test is nice because it ensures that the consumer won't have to worry about nulls (I might need to add a few more tests to guarantee that). Let's face it though, what I'm really after is making sure GetAllIndicators() works well with my data access layer! So I'm going to take a big step - maybe too big, but it's what I think is necessary:

[Test]
public void CanGetAllIndicators()
{
   AddIndicator("Test Name 1");
   AddIndicator("Test Name 2");

   List<Indicator> results = Indicator.GetAllIndicators();
   Assert.AreEqual(2, results.Count);
   Assert.AreEqual("Test Name 1", results[0].Name);
}


private void AddIndicator(string name)
{
 
//todo
}


Ok, so my test fails, and I have a lot of work ahead of me (relative to what I've had to do so far) to get it working. I still think I'm taking on more than I should - but once I get this infrastructure set up, it'll be easier to do other similar test. (Not only do I feel like I'm taking too big a step, but I also feel like I'm jumping into infrastructure way too early, still, I see real value here!) First though, I realize that my 2nd assert is wrong. I don't have a requirement for a first-in first-out, I'll probably have a different default ordering, so I just take it out and do something like:

[Test, Ignore]
public void GettingAllIndicatorsIsInCorrectOrder()
{
}


Back to the main test. I'll be using a simple DataProvider class that follows the much overhyped provider pattern. I want to do something like DataProvider.Instance.GetAllIndicators(). So I change my Indicator.GetAllIndicator() code to do just that:

public static List<Indicator> GetAllIndicators()
{
  DataProvider.Instance.GetAllIndicators();
}


I'm back with a broken build. I create an abstract DataProvider class, add an Instance property and declare an abstract GetAllIndicators function. Here's what it might look like:

internal abstract class
DataProvider
{
   private DataProvider _instance;
   public DataProvider
   {
      get { return _instance; }
   }

   internal abstract List<Indicator> GetAllIndicators();
}


So the real gem here, as far as I'm concerned, is that I don't have to build a SqlDataProvider class yet. I can simply build a TestDataProvider class, put a minimal amount of effort to get it up and running and focus again on my domain:

internal class TestDatarovider : DataProvider
{
   private List<Indicator> _indicators = new List<Indicator>();
   internal override List<Indicator> GetAllIndicators()
   {
      return _indicators;
   }
}


Things are starting to come together, but there are two really important things I still need to do in order to get a test that'll pass. First, I need to be able to add indicators, second I need to get the DataProvider to return an instance of TestDataProvider. At first I was tempted to simply create a new abstract method "AddIndicator" to my DataProvider and implement it. Problem is, this system doesn't take care of adding indicators, it only reads them, and I'm not a fan of burdening my entire system just for the sake of being able to test properly. My solution is to add the AddIndicator method directly into my TestDataProvider, not the base DataProvider class:

internal void AddIndicator(Indicator indicator)
{
   _indicators.Add(indicator);
}


Now, to inject my DataProvider with my TestDataProvider, I expose a simple function to overwrite _instance:

internal static void OverwriteInstance(DataProvider newInstance)
{
   _instance = newInstance;
}


I can now create a Setup method in my test:

private TestDataProvider _dataProvider;
[SetUp]
public void FixtureSetup()
{
   _dataProvider = new TestDataProvider();
   DataProvider.OverwriteInstance(_dataProvider);         
}


This not only ensures that DataProvider.Instance always returns a TestDataProvider, but also makes sure that each tests runs in a clean environment (a new instance of TestDataProvider is created for each test). Finally, coding my AddIndicator helper method and running my test gives me a pass:

private void
AddIndicator(string name)
{
  _dataProvider.AddIndicator(new Indicator(name));
}

To recap:
  • We used the provider pattern so that we can easily inject a stub
  • We've created a stub of our data access layer so that we can continue to focus on our domain
  • We had to create extra functionality (adding an indicator), but kept that isolated in our test classes
Technorati Tags: , ,



Published Jul 05 2006, 01:34 PM by karl
Filed under:

Comments

Mark Brackett said:

I like the way you solved the issue of getting data into the provider (by declaring your test required AddIndicator directly on the TestDataProvider class) - but think the DataProvider.OverwriteInstance is a little odd. Is there a requirement that you be able to change DataProviders during runtime?

I think the OverwriteInstance method just highlights the fact that you just created a global
http://butunclebob.com/ArticleS.MichaelFeathers.TheReluctantGlobalVariable

Why not move to dependency injection, and inject the Test|SqlDataProvider into the Indicator class? If not needed for other functions, how about passing it as a param into the GetAllIndicators?

As a static, if you use DataProvider in other classes, you have to be concerned with locking - which adds code complexity and would effectively serialize your database communications. With an instance per class, you get rid of locking and (if using multiple threads) could potentially run more than 1 load query at a time.
# July 5, 2006 8:55 PM

Joshua Flanagan said:

I second the suggestion to investigate dependency injection (there are a few good articles right here on CodeBetter).

Also, check out one of the mocking frameworks (Rhino Mocks, NMock). They may look a little confusing at first, but they are well worth the effort to learn.
# July 5, 2006 9:59 PM

karl said:

Thanks guys, will look into it. NMock is definetly on my radar ...hoping to get into it early next week and blog about my experience :)
# July 5, 2006 10:36 PM

Jeremy D. Miller said:

NMock had its day, but go straight to RhinoMocks.  NMock relies on strings where RhinoMocks is strongly-typed, "ReSharper-able" code
# July 5, 2006 11:48 PM

Sam Gentile said:

I had yesterday's N&amp;amp;N cut short with a trip to the Jersey Shore. After a fine cookout day yesterday,...
# July 6, 2006 12:10 AM

Leave a Comment

(required)  
(optional)
(required)  

Enter the numbers above:
Add
Check out Devlicio.us!

Our Sponsors

Free Tech Publications