Code Coverage on Constructor Based Dependency Injection

I was working with nCoverExplorer at work this week checking to see how diligent we'd been while writing our tests.  One of our major tasks in the last month has been to refactor our service layer so that it employs constructor based dependency injection.  When I looked through the coverage on this work I noticed that the coverage was lower than I'd been hoping for.  Closer inspection showed that the culprit was the constructor chaining we were using as part of our Dependency Injection refactoring. 

Below is an example of a class that we would create.

namespace DependencyInjectionExample
{
    public class DIExample
    {
        private readonly IDependency _dependency1;
        private readonly IDependency _dependency2;

        public DIExample():this(new SomeDependency(), new AnotherDependency()){}

        public DIExample(IDependency dependency1, IDependency dependency2)
        {
            _dependency1 = dependency1;
            _dependency2 = dependency2;
        }

        public void SomeMethod(string value)
        {
            _dependency1.DoStuff(value);
            _dependency2.DoStuff(value);
        }
    }
}
And here is an example of how we would test it.
using MbUnit.Framework;
using Rhino.Mocks;

namespace DependencyInjectionExample
{
    [TestFixture]
    public class DIExampleTests
    {
        private MockRepository _mockery;

        [TestFixtureSetUp]
        public void Setup()
        {
            _mockery = new MockRepository();
        }

        [TestFixtureTearDown]
        public void TearDown()
        {
            _mockery.VerifyAll();
        }

        [Test]
        public void SomeMethod_ShouldCallAppropriateMethods()
        {
            IDependency mockDependency1 = (IDependency) _mockery.CreateMock(typeof (IDependency));
            IDependency mockDependency2 = (IDependency) _mockery.CreateMock(typeof (IDependency));

            mockDependency1.DoStuff(string.Empty);
            LastCall.IgnoreArguments().Repeat.Once();
            mockDependency2.DoStuff(string.Empty);
            LastCall.IgnoreArguments().Repeat.Once();

            _mockery.ReplayAll();

            DIExample diExample = new DIExample(mockDependency1, mockDependency2);
            diExample.SomeMethod(string.Empty);
        }
    }
}
When you run this test through nCover you get this.

ConstructorCoverage

Because my only test, which I believe to be the only one necessary, uses the parameterized override of the constructor, the default constructor does not get executed.  In this example that means that code coverage drops from 100% to 78% which is below the threshold of 90% that I strive for.  I could easily write another test that creates the object using the default constructor, but I don't see why I'd write that test.  In my mind it doesn't bring any benefit to the test suite.  I realize that code coverage metric are not metrics of quality and this is why I don't see the benefit.

My question to all of you is this:  What do you do in this situation?

posted @ Friday, April 06, 2007 6:17 PM

Print

Comments on this entry:

# re: Code Coverage on Constructor Based Dependency Injection

Left by Tom Opgenorth at 4/6/2007 9:59 PM
Gravatar
I'd either (a) not worry about it because it's probably not worth the effort or (b) refactor: You said that one of your goals was to refactor to use constructor based dependency injection, so... Get rid of the default constructor. Make everything everywhere use a parameter based constructors. I hear you now: Tom, stop drinking the cheap, blended scotch. For compatibility reasons, we need the default constructor to provide default dependencies. To this, I propose the following: 1. Mark the default constructor with the [Obsolete] attribute. Then you can see where the default constructors are used. Make a list and have your least favourite developer start on cleaning up these warnings (that will probably involve getting Justice hired). 2. Create a private method, something like : private void Initialize(IDependency dependency1, IDependency dependency2) { _dependency1 = dependency1; _dependency2 = dependency2; } 3. Have all constructors call this new private method, so: public DIExample(IDependency1 dependency1, IDependency2 dependency2) { Initialize(dependency1, dependency2); } 4. Use a dependency injection framework (Castle Windsor maybe) in the default constructor: public DIExample() { IDependency1 dep1 = _windsor["Dependency1"] as IDependency; IDependency2 dep2 = _windsor["Dependency2"] as IDependency; Initialize(dep1, dep2); } Now, of course, this does add extra complexity, but you didn't ask for a simple solution. :)

# Interesting finding - 04/06/2007 « Another .NET Blog

Gravatar
PingBack from http://liangwu.wordpress.com/2007/04/07/interesting-finding-04062007/

# Interesting Finds: April 7, 2007

Left by Jason Haley at 4/7/2007 8:35 AM
Gravatar

# Interesting Finds: April 7, 2007

Left by Jason Haley at 4/7/2007 8:37 AM
Gravatar

# re: Code Coverage on Constructor Based Dependency Injection

Left by Justice~! at 4/7/2007 10:40 PM
Gravatar
GGGGHH! Jason Haley *AGAIN*. You get all the love man! What is the secret???

# re: Code Coverage on Constructor Based Dependency Injection

Left by Steven R at 4/8/2007 10:55 AM
Gravatar
If you can't go with Tom's approach I would say that it is worth the effort to write a test for the default constructor since it won't take you long to write the test and it is going to be the constructor that everyone uses.

# re: Code Coverage on Constructor Based Dependency Injection

Left by Tom Opgenorth at 4/8/2007 11:43 AM
Gravatar
Actually, as I think about it, you could cheat a bit: Rather that using some sort of IoC container in the default constructor, you could also just go: [Obsolete("Should use the more greedy constructor")] public DIExample() { IDependency1 dep1 = new ConcreteDependency1(); IDependency2 dep2 = new ConcreteDependency2(); Initialize(dep1, dep2); }

# re: Code Coverage on Constructor Based Dependency Injection

Left by Mrs Loquacious at 4/8/2007 4:42 PM
Gravatar
Um...what is a dependency injection? I thought it was slang for something perverted! ;)

# re: Code Coverage on Constructor Based Dependency Injection

Left by The Coding Hillbilly at 4/11/2007 10:44 AM
Gravatar
I'm with you, Mrs. L. I don't understand how these guys can say the word injection with adding "tee hee" to the end of it. Anyway, don't write code just to meet metric targets. That makes you a manager. If you can't justify testing the default constructor, either dump it or revise your metrics. But are you sure the test isn't warranted?

Your comment:



 (will not be displayed)


 
 
 
Please add 7 and 8 and type the answer here:
 

Live Comment Preview: