Why factories with configuration files are better for decoupling but still a testability killer
5 min read
5 min read
...The according code using the factory then looks as follows
<add requestedType="CompanyName.ProjectName.BusinessLogicInterface.INationBL, CompanyName.ProjectName.BusinessLogicInterface" fetcher="Singleton" paramType="CompanyName.ProjectName.BusinessLogic.NationBL, CompanyName.ProjectName.BusinessLogic"/>
...
...According to the above mentioned configuration, your interface
INationBL nationBl = BLFactory.Instance.Get<INationBL>();
...
INationBL
will be associated with an instance of type NationBL
. I don't know whether you already got the advantage here: by having a configuration based factory and by only relying on interfaces, you externalize you dependencies from the code and you defer the resolving of the correct type to the moment of code execution.Nation
object....To verify this, a possibility is to test the following scenarios
public Nation GetItalianNation()
{
Nation result = null;
INationBL nationBl = BLFactory.Instance.Get<INationBL>();
ListallNations = nationBl.ReadAllNations();
if(allNations != null)
{
foreach (Nation nation in allNations)
{
if(nation.CountryCode == Nation.TypeOf.CountryCodeItaly)
{
result = nation;
break;
}
}
}
return result;
}
...
allNations
contains multiple entries, one of them being an italian Nation meaning the nation.CountryCode
is equal to "300" (for example) which is represented by the constant Nation.TypeOf.CountryCodeItaly
.INationBL
object on which the ReadAllNations()
method is invoked for retrieving the list of nations (from the DB). This is where our logic has to take place. Now given that we have our nice decoupling through our config-based factories we can place our own MockNationBl class.public class MockNationBl : INationBLNow this generic mock let's us pretty easy do the testing job. Inside our testing project (you should have a different project for your tests!!) we create the factory mappings in the config file but instead of returning an instance of
{
//these are used to modify the nations collection
private IList<Nation> _Nations;
public IList<Nation> Nations
{
get
{
return _Nations;
}
set
{
_Nations = value;
}
}
...
public IList<Nation> ReadAllNations()
{
return _Nations;
}
...
}
NationBL
we return an instance of our MockNationBl
....Something like the above. For those who still don't see the benefit of using interfaces...well this is it. You can easily switch the concrete object instances (see strategy pattern which exploits this concept). When launching the unit test and calling the
<add requestedType="CompanyName.ProjectName.BusinessLogicInterface.INationBL, CompanyName.ProjectName.BusinessLogicInterface" fetcher="Singleton" paramType="CompanyName.ProjectName.BusinessLogic.MockNationBl, CompanyName.ProjectName.UnitTests"/>
...
GetItalianNation()
method an instance of the MockNationBl
will be used.INationBL
to be instantiated with another mock, not yours?? He just cannot.public class SomeClassWith this little modification the code gets much cleaner, the instance of an INationBL (if used multiple times) gets fetched/reused from a single place within the class, by default using the factory. In a possible test setup we can however easily create our mock implementation and "inject" it:
{
private INationBL _NationBl;
public INationBL NationBl
{
get
{
if(_NationBl == null)
_NationBl = BLFactory.Instance.Get<INationBL>();
return _NationBl;
}
set
{
_NationBl = value;
}
}
public Nation GetItalianNation()
{
Nation result = null;
List allNations = _NationBl.ReadAllNations();
if(allNations != null)
{
foreach (Nation nation in allNations)
{
...
}
}
return result;
}
}
...
[TestInitialize()]
public void SetUp()
{
INationBL mockNationBL = new MockNationBL();
someClass.NationBL = mockNationBl;
...
}
...