Why factories with configuration files are better for decoupling but still a testability killer

As I already highlighted in several previous posts one of the most critical things when facing unit testing is an appropriate design. My sensation is that those rejecting unit tests...
  • do not care about code quality
  • are often poor programmers in terms of the code they produce (poor code design) and have therefore difficulties in writing automated tests for it
  • do not see the value behind them but still see them as just "more" work to do.
Anyway, without going into details on this, I'd like to highlight the poor code design problem since that's probably one of the most prevalent reasons for people failing to write appropriate tests. Let's look at a "real-world" scenario again.

Assume you really care about software architecture and design. You use interfaces for nicely decoupling the different system functionalities where the concrete object instances are being resolved at runtime by using factories. These factories access a configuration file (i.e. the web.config in ASP.net) for retrieving the type information and hosting DLL and then, through reflection, fetch the specific instance that has been configured for a given interface.
An entry in the config file could look like this:
...
<add requestedType="CompanyName.ProjectName.BusinessLogicInterface.INationBL, CompanyName.ProjectName.BusinessLogicInterface" fetcher="Singleton" paramType="CompanyName.ProjectName.BusinessLogic.NationBL, CompanyName.ProjectName.BusinessLogic"/>
...
The according code using the factory then looks as follows
...
INationBL nationBl = BLFactory.Instance.Get<INationBL>();
...
According to the above mentioned configuration, your interface 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.
This is good practice because by avoiding dependencies, you decouple which in turn will make life easier when writing automated tests against this logic.

Why is this?
Well, what we usually want when writing unit tests is to verify the correctness of a specific logic in isolation. So one of the things we do is to mock out undesirable object dependencies (like network resource access, DB access etc..). Assume the following sample code has to be tested. We want to verify whether it correctly returns the italian Nation object.
...
public Nation GetItalianNation()
{
Nation result = null;

INationBL nationBl = BLFactory.Instance.Get<INationBL>();
List allNations = nationBl.ReadAllNations();
if(allNations != null)
{
foreach (Nation nation in allNations)
{
if(nation.CountryCode == Nation.TypeOf.CountryCodeItaly)
{
result = nation;
break;
}
}
}

return result;
}
...
To verify this, a possibility is to test the following scenarios
  • check for "null" result
    • allNations contains no objects at all
    • allNations contains multiple Nation objects, none of them however satisfying the condition of the nation.CountryCode.
  • check for a valid italian Nation object: 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.
To realize our testing strategy we have to assure that allNations get's properly filled. As you see, the code piece above fetches an instance of a 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 : INationBL
{
//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;
}
...
}
Now 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 NationBL we return an instance of our MockNationBl.
...
<add requestedType="CompanyName.ProjectName.BusinessLogicInterface.INationBL, CompanyName.ProjectName.BusinessLogicInterface" fetcher="Singleton" paramType="CompanyName.ProjectName.BusinessLogic.MockNationBl, CompanyName.ProjectName.UnitTests"/>
...
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 GetItalianNation() method an instance of the MockNationBl will be used.

So what about the title. These factories should be a good thing, shouldn't they? Why "testability killer"?
Well "testability killer" is quite provocant actually :) . What I don't like about the above mentioned strategy is the overhead these factories create during testing. Config files are good for "outsourcing" configuration to a central place. This is what they are good for, but still, people tend to forget them or how to correctly set them. Moreover they limit your possibilities: what about if another developer needs his INationBL to be instantiated with another mock, not yours?? He just cannot.

So what I'd like to have is an environment where concrete object instances are being retrieved from configuration files if you run your system, but still give you enough flexibility to inject your own ones during testing. May sound complex but it actually isn't. This is how DI frameworks basically work. To adapt the code above we don't have to change much, but just delegate the fetching of the concrete type to a class property as follows:
public class SomeClass
{

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;
}
}
With 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:
...
[TestInitialize()]
public void SetUp()
{
INationBL mockNationBL = new MockNationBL();
someClass.NationBL = mockNationBl;
...
}
...
In this way you don't even need a configuration file in your test project. DI frameworks work similar, what I like about them is that they work according to the "hollywood principle". The impact on your written code is very small.
Kindle

Comments

0

Your ad here?