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: Agile, TDD, .NET