On my quest to design for testability, I've already covered:
(In case you don't know what IoC containers are, I advise you to take a peek at Part 4 of this series first before continuing with this article.In case you are not familiar with Microsoft Unity IoC framework which I would be using in this post read part 7)
Intro
Before I start, I would like to recommend you to subscribe to Alt.NET mail list where you have daily top notch discussions about various ultra interesting subjects.
One of April subjects was Are Opaque Depencies Bad? (Mockist TDD's effect on design) where you can see that some of very smart people were stating that Opaque dependencies are sometime acceptable compromise because they encapsulate complexity and the component dependency information we get anyhow are not that valuable.
IMHO that is wrong because of the reasons I would state in this post and which can be summarized as:
- writing tests for components implementing opaque IoC style is much harder
- transparent IoC helps identifying controls which “do too much” – in general the one with very big number of dependencies are maybe candidate for investigating if there is SoC violation
Opaque dependencies
In most of code in the same example I was using in previous posts in design for testability series, I was writing code similar to this:
using System;
using System.Collections.Generic;
using Facade;
namespace Example.UserManager
{
public class UserManager : IUserManager
{
#region IUserManager Members
public int NumberOfUsersActiveInLast10Days(string userName)
{
IUserProvider userProvider=ServiceLocator.Retrieve<IUserProvider>() ;
IList<User> userCollection = userProvider.GetUserCollection();
int result = 0;
foreach (User user in userCollection)
{
if (user.Name.StartsWith(userName)
&& user.LastActivity > DateTime.Now.AddDays(-10))
result++;
}
return result;
}
#endregion
}
}
In first line of my the NumberOfUsersActiveInLast10Days method, an instance of type implementing IUserProvider has been retrieved and then used it in next line to get list of all users.
Once the list of users would be retrieved from DAL provider class, method code iterates and filters out all of the users which name starts with given string and which were active in last 10 days.
As you can see the usage of service locator is “hidden” into the method code and invincible outside of that method.
In other words, if we would check out the code using that method
using System;
using Example.UserManager;
using Facade;
namespace Driver
{
internal class Program
{
private static void Main(string[] args)
{
UserManager userManager=new UserManager();
int activeUserCount = userManager.NumberOfUsersActiveInLast10Days(”DB”);
Console.WriteLine(activeUserCount);
Console.ReadKey();
}
}
}
we wouldn’t get any clue that UserManager has any dependency on ServiceLocator and UserManagerProvider.
This style of implicit usage of IoC framework where IoC framework is embed directly into the members using it, is called opaque dependency injection.
According to my personal experience opaque style is something looking very appealing to people new to IoC because it is a way of implementing IoC with “nothing changed”, so usually it is considered as “acceptable compromise” :)
Testing the opaque dependencies
Let see why I found this opaque style bad on example of how making test for this NumberOfUsersActiveInLast10Days method would look.
The test I would write would have to verify that for no users in database which have username starting the with given string, program would return zero.
I am sitting in black box testing camp (no testing of private method and looking at the code being tested maximally discouraged) so I usually start my tests writing the playback code
[Test]
public void GetActiveUsers_TestCaseOfZeroUsers_WouldReturnEmptyCollection()
{
using (mockRepository.Record())
{
// no clear idea what to mock here
}
using (mockRepository.Playback())
{
var userManager = new UserManager();
int numberOfUsers= userManager.NumberOfUsersActiveInLast10Days(“X”);
Assert.IsTrue(numberOfUsers == 0);
}
}
Well, as you can see after writing the playback code representing the ways how the code would be used, I don’t have clear idea what I should mock in record section so the test wouldn’t fail.
The only way how I can write the test is to run the test, get error
System.InvalidOperationException: The current type, Example.UserManager.IUserProvider, is an interface and cannot be constructed. Are you missing a type mapping?
then add code to record phase mocking that, run the test again, check out additional error messages etc.
Not very pleasant and productive way to get the final test result which in case of opaque dependency looks like
[Test]
public void GetActiveUsers_TestCaseOfZeroUsers_WouldReturnEmptyCollection()
{
using (mockRepository.Record())
{
var userProvider = mockRepository.DynamicMock<IUserProvider>();
Expect.Call(userProvider.GetUserCollection())
.Return(new List<User>());
ServiceLocator.InjectStub(userProvider);
}
using (mockRepository.Playback())
{
var userManager = new UserManager();
int numberOfUsers= userManager.NumberOfUsersActiveInLast10Days(“X”);
Assert.IsTrue(numberOfUsers == 0);
}
}
Transparent dependencies
Transparent dependency injection style requires explicit definition of dependencies in class constructor, exposing clearly what are the dependency class has.
UserManager class written in this way would look like this
using System;
using System.Collections.Generic;
using Facade;
namespace Example.UserManager
{
public class UserManager : IUserManager
{
private readonly IUserProvider _userProvider;
public UserManager():this(ServiceLocator.Retrieve<IUserProvider>())
{ }
internal UserManager(IUserProvider userProvider)
{
_userProvider = userProvider;
}
#region IUserManager Members
public int NumberOfUsersActiveInLast10Days(string userName)
{
IList<User> userCollection = _userProvider.GetUserCollection();
int result = 0;
foreach (User user in userCollection)
{
if (user.Name.StartsWith(userName)
&& user.LastActivity > DateTime.Now.AddDays(-10))
result++;
}
return result;
}
#endregion
}
}
If we look at the NumberOfUsersActiveInLast10Days method we would see that ServiceLocator class is not been used nay more there. Method now uses userProvider field without knowing how the _userProvider field is been initialized.
We have also two constructors:
- Internal constructor accepting the parameter of IUserProvider data type and store its value into the _userProvider field
- Public default constructor not accepting any parameters but which uses the ServiceLocator to retrieve from IoC container service implementing the IUserProvider interface and then calling internal constructor passing that service to internal constructor parameter
“Dude, this is no transparent dependency…”
Usually examples I was finding on net wouldn’t look like the just given example. The internal constructor would become public and the current default constructor doing poor man dependency injection through ServiceLocator wouldn’t exist at all.
Their rationale is that the UserManager would always be constructed using IoC framework which would automatically perform the injection during the IoC container construction time.
In real enterprise development that is often not true because we could have in existing code base a lot of places doing already doing UserManager mgr=new UserManager() (constructing without the usage of IoC) so that implicit IoC dependency injection wouldn’t work for them.I guess also that updating all of the current code just to be able to serve the IoC framework wouldn’t be probably very high on “resource allocation list“ spreadsheet, so we could count on the fact that this “legacy” code would stay like it is now.
What I am doing in my approach is basically switching that implicit IoC dependency injection with explicit one. The major difference here is that I am basing my IoC class design on default constructor and not on some specialized one. That’s why when code construct the class, in both cases (using IoC or new keyword) the result is the same – automatic service resolution and dependency injection. In other words, all this would work with any legacy code using the UserManager class.
Another significant gain (IMHO) from my approach is that I am hiding the complexity from production code (only default constructor exposed) while still having the same IoC concepts in place.
After all, having class design “open for testing but closed for code noise” is the whole point of this DTF blog post series.
“Dude, this is transparent dependency, just it is V2” :)
Testing the transparent dependencies
Once we have our code restructured testing is much more easier and even possible without using the ServiceLocator at all.
Writing the test in this case would show me during the Playback definition that UserManager class have a constructor accepting the IUserProvider class which would lead me to the conclusion what I need to mock in order to have this test done without “run until not complaining” approach I described in opaque test example above.
So first we would add to AssemblyInfo.cs file of UserManager appropriate assembly attribute
[assembly: InternalsVisibleTo("UserManager.Test")]
After this User.Test assembly would get access to UserManager internal constructor above so the test would look like this
[Test]
public void GetActiveUsers_TestCaseOfZeroUsers_WouldReturnEmptyCollection()
{
IUserProvider userProvider = mockRepository.DynamicMock<IUserProvider>();
using (mockRepository.Record())
{
Expect.Call(userProvider.GetUserCollection())
.Return(new List<User>());
}
using (mockRepository.Playback())
{
var userManager = new UserManager(userProvider);
int numberOfUsers= userManager.NumberOfUsersActiveInLast10Days(“X”);
Assert.IsTrue(numberOfUsers == 0);
}
}
Couple of things to note in this example:
- I am not using ServiceLocator at all for implementing this test.
- I am using internal UserManager constructor to inject UserProvider mock.
- While writing the line using the internal constructor I realized (from constructor signature) that I need mocked instance of IUserProvider (first line of the test method)
Conclusion
IMHO, using of transparent dependency significantly simplifies the test making. The approach here I presented (with internal constructor) is variation of standard implementation made to hide the complexity and remove the rope from the hands of TDD unskilled developers. Applying it could enable having a small “TDD core team” of people doing component level design and enabling IoC (with all of the benefits) and all other developers totally unaware of the IoC class design which should reduce training costs and increase IoC adoption chances in organizations not applying it.
What's next?
My next blog post would be on how AutoMockingContainer (AMC) makes testing easier in cases we are using transparent dependency injection style so in a sense with this blog post makes complete explanation on how to test IoC based components done with transparent dependency injection style
Source code of the examples (opaque and transparent) presented in this post can be found here