Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Unit testing and dependency injection with deeply nested dependencies

Assume a legacy class and method structure like below

public class Foo
{
    public void Frob(int a, int b)
    {
        if (a == 1)
        {
            if (b == 1)
            {
                // does something
            }
            else
            {
                if (b == 2)
                {
                    Bar bar = new Bar();
                    bar.Blah(a, b);
                }
            }
        }
        else
        {
            // does something
        }
    }
}

public class Bar
{
    public void Blah(int a, int b)
    {
        if (a == 0)
        {
            // does something
        }
        else
        {
            if (b == 0)
            {
                // does something
            }
            else
            {
                Baz baz = new Baz();
                baz.Save(a, b);
            }
        }
    }
}

public class Baz
{
    public void Save(int a, int b)
    {
        // saves data to file, database, whatever
    }
}

And then assume management issues a nebulous mandate to perform unit testing for every new thing we do, be it an added feature, modified requirement, or bug fix.

I may be a stickler for literal interpretation, but I think the phrase "unit testing" means something. It does not mean, for example, that given inputs of 1 and 2 that the unit test of Foo.Frob should succeed only if 1 and 2 are saved to a database. Based on what I've read, I believe it ultimately means based on inputs of 1 and 2, Frob invoked Bar.Blah. Whether or not Bar.Blah did what it is supposed to do is not my immediate concern. If I'm concerned with testing the entire process, I believe there's another term for that, right? Functional testing? Scenario testing? Whatever. Correct me if I'm being too rigid, please!

Sticking with my rigid interpretation for the moment, let's assume I want to try to utilize dependency injection, with one benefit being that I can mock away my classes so that I can, for example, not persist my test data to a database or file or whatever the case may be. In this case, Foo.Frob needs IBar, IBar needs IBaz, IBaz may need a database. Where are these dependencies to be injected? Into Foo? Or does Foo merely need IBar, and then Foo is responsible for creating an instance of IBaz?

When you get into a nested structure such as this, you can quickly see there could be multiple dependencies necessary. What is the preferred or accepted method of performing such injection?

like image 540
Anthony Pegram Avatar asked Nov 10 '10 17:11

Anthony Pegram


Video Answer


3 Answers

Let us start with your last question. Where are the dependencies injected: A common approach is to use constructor injection (as described by Fowler). So Foo is injected with an IBar in the constructor. The concrete implementation of IBar, Bar in turn has an IBaz injected into its constructor. And finally the IBaz implementation (Baz) has an IDatabase (or whatever) injected. If you use a DI framework such as Castle Project, you would simply ask the DI container to resolve an instance of Foo for you. It will then use whatever you have configured to determine which implementation of IBar you are using. If it determines that your implementation of IBar is Bar it will then determine which implementation of IBaz you are using, etc.

What this approach gives you, is that you can test each of the concrete implementations in isolation, and just check that it invokes the (mocked) abstraction correctly.

To comment on your concerns about being too rigid etc, the only thing I can say is that in my opinion you are choosing the right path. That said, management might be in for a surprise when the actual cost of implementing all those tests becomes apparent to them.

Hope this helps.

like image 115
Klaus Byskov Pedersen Avatar answered Sep 22 '22 18:09

Klaus Byskov Pedersen


the kind of test you described in the first part of your post (when you try all the parts together) it is usually defined as integration test. As a good practice in your solution you should have either a unit test project and an integration test project. In order to inject dependecies in your code the first and most important rule is to code using interfaces. Assumed this, let's say your class contains an interface as a member and you want to inject/mock it: you can either expose it as a property or pass the implementation using the class constructor. I prefer to use properties to expose dependencies, this way the constructor don't become too verbose. I suggest you to use NUnit or MBunit as a testing framework and Moq as a mocking framework (more clear in it's outputs than Rhino mocks) Here's the documentation with a some examples on how to mock with Moq http://code.google.com/p/moq/wiki/QuickStart

Hope it helps

like image 29
Giorgio Minardi Avatar answered Sep 22 '22 18:09

Giorgio Minardi


I don't think there is one "preferred" method for addressing this, but one of your main concerns seems to be that with dependency injection, when you create Foo, you need to also create Baz which might be unnecessary. One simple way around this is for Bar not to depend directly on IBaz but on a Lazy<IBaz> or a Func<IBaz>, allowing your IoC container to create an instance of Bar without immediately creating Baz.

For example:

public interface IBar
{
    void Blah(int a, int b);
}

public interface IBaz
{
    void Save(int a, int b);
}

public class Foo
{
    Func<IBar> getBar;
    public Foo(Func<IBar> getBar)
    {
        this.getBar = getBar;
    }

    public void Frob(int a, int b)
    {
        if (a == 1)
        {
            if (b == 1)
            {
                // does something
            }
            else
            {
                if (b == 2)
                {                        
                    getBar().Blah(a, b);
                }
            }
        }
        else
        {
            // does something
        }
    }
}



public class Bar : IBar
{
    Func<IBaz> getBaz;

    public Bar(Func<IBaz> getBaz)
    {
        this.getBaz = getBaz;
    }

    public void Blah(int a, int b)
    {
        if (a == 0)
        {
            // does something
        }
        else
        {
            if (b == 0)
            {
                // does something
            }
            else
            {
                getBaz().Save(a, b);
            }
        }
    }
}

public class Baz: IBaz
{
    public void Save(int a, int b)
    {
        // saves data to file, database, whatever
    }
}
like image 36
Mark Heath Avatar answered Sep 21 '22 18:09

Mark Heath