Introduction
I’m not going to proselytize unit testing in this blog post, there are hoards of articles on the internet for that already. Let’s just agree, for arguments sake, that they do awesome things for any software project. In spite of this majesty, brittle unit tests can suck the life out of programmers. There are many reasons for test frailty (a topic for another time), but the main one I’m going after in this piece is adding dependencies. In short, adding a new dependency to any class makes the existing tests fail. Is there any way of getting around this? Yes, there is!
As a side note: In a perfect world, software would be architected so elegantly that adding dependencies would be unnecessary. Unfortunately, I don’t live in that world. If you know how to get there, please send me directions! That being said, adding dependencies may be a sign that you aren’t adhering to the single responsibility principal. Just something to keep in mind… Regardless, the pattern laid out below has made my life easier.
The Developer and the Brittle Test
Once upon a time, in a land far far away, our hero writes the following really interesting service.
public class ReallyInterstingService
{
private readonly IDoSomethingAwesome _doSomethingAwesome;
public ReallyInterstingService(IDoSomethingAwesome doSomethingAwesome)
{
this._doSomethingAwesome = doSomethingAwesome;
}
public string InterstingMethod()
{
return this._doSomethingAwesome.ToString();
}
}
This really interesting service needs tests. Our hero, being a true hero, used TDD so he wrote the tests below before ever writing the service. Problem solved!
[TestClass] public class ReallyInterstingServiceShould
{
[TestMethod] public void ReturnString()
{
var awesomeMock = new Mock<IDoSomethingAwesome>();
var sut = new ReallyInterstingService(awesomeMock.Object);
var result = sut.InterstingMethod();
Assert.IsInstanceOfType(result, typeof(string));
}
}
The tests are fantastic! Anyone can refractor without fear. Life is good! However, as with any great story, along comes the antagonist: the evil manager. Management demands our hero to befoul really interesting service with some inanity. After a long battle, our hero concedes and makes the modifications below.
public class ReallyInterstingService
{
private readonly IDoSomethingAwesome _doSomethingAwesome;
private readonly IDeathToAwesome _deathToAwesome;
public ReallyInterstingService(IDoSomethingAwesome doSomethingAwesome,
IDeathToAwesome deathToAwesome)
{
this._doSomethingAwesome = doSomethingAwesome;
this._deathToAwesome = deathToAwesome;
}
public string InterstingMethod()
{
return this._doSomethingAwesome.ToString();
}
public string LoadOfTripe()
{
return this._deathToAwesome.ToString();
}
}
As you can see, a new dependency was added and now all the beautiful tests have failed. Woe to the kingdom. Our hero is vexed! How could he have foreseen this tragedy? What can he do to protect the program against such calamity in the future?
After a long quest, our hero stumbles upon the humble author’s DotNetTestHelper open source project. It’s an incredibly simple library still in its infancy, but it has something wonderful: SutBuilder! He refractors the test as follows:
[TestClass] public class ReallyInterstingServiceShould
{
[TestMethod] public void ReturnString()
{
var awesomeMock = new Mock<IDoSomethingAwesome>();
var sut = new SutBuilder<ReallyInterstingService>() .AddDependency(awesomeMock.Object) .Build();
var result = sut.InterstingMethod(); Assert.IsNotNull(result);
Assert.IsInstanceOfType(result, typeof(string));
}
}
What wizardry is this, say you? The SUT was constructed without the added dependency! The test is also protected against added dependencies in the future. The kingdom is saved! They lived happily ever after.
The End
Ok Ok Ok, that was a bit absurd. Anyway…
SutBuilder
is a utility I wrote as part of an open source project of .NET testing utilities. The full source code is available here. What it does is simple: it constructs an instance of the generic type parameter. See the code below.
These two statements have identical results:
sut = new ReallyInterstingService( new Mock<IDoSomethingAwesome>().Object,
new Mock<IDeathToAwesome>().Object);
sut = new SutBuilder<ReallyInterstingService>().Build();
Note: I’m using Moq to create mocks. I really dig that framework! It’s free and easy to use.
SutBuilder
finds the constructor with the most arguments and uses it for instantiation. For each constructor argument, SutBuilder
determines if an object of the argument type was passed in via the AddDependency
method. If it was, it will pass that object to the constructor, otherwise it will pass an empty Moq mock of that type. AddDependency
returns an instance of SutBuilder
to provide a nice fluent API. See the code below.
IDoSomethingAwesome somethingAwesome = new DoSomethingAwesome();
sut = new ReallyInterstingService( somethingAwesome, new Mock<IDeathToAwesome>().Object);
sut = new SutBuilder<ReallyInterstingService>().AddDependency(somethingAwesome).Build();
All the magic happens in the Build
method where it returns the created instance. That’s all there is to it!
As mentioned above, DotNetTestHelper is in its infancy. I will entertain any and all pull requests!
Thank you for reading!
CodeProject