Sunday, 6 April 2014

VS 2012 Fakes - Testing the Untestable

I have always been the great fan of TDD approach of coding. But recently, I've ran into case with a situation when the code was not Testable. Before we go ahead into further details I would like to put a disclaimer.

This is not an en talk but I'm expecting you are:
1. Familiar with Unit testing
2. Mocking and faking the real dependencies
3. Writing testable code

 

If you agree with above Agreement then I'm sure you'll enjoy this reading.
We Write testable code to make the external dependencies injectable via dependcy injection and faking/mocking them while unit testing. But what if:
1.  you have too many dependencies and you can't take put like 6-7 things to inject via constructor. Ofcourse it will look ugly in the code.
2. you have some dependecies that you can't fake. So you wrap the dependency in one of your own interface with implementing the real deal. But this is too much of writing code for getting rid of that thing.
3. You have to test the Legacy code that is Either "Restricted" for refactoring or "Refactoring" Cost you a lot as stakeholders don't want you put that much efforts in it.


As mentioned above, You might be having enough reasons to stuck and thinking what should I DO NOW?????

 

 

 


Let's take you a hope of life and let's discuss about the new Visual Studio 2012 Fakes feature which available in Premium and above versions only. That's enough of explanation I won't deep dive into how it comes into the picture blah blah.. but this would be your plan B for survival. So let's take a sample of code and finding the problem on the way:

I have this simple class that reads the configuration appsetting for reading the connectionString from Appsetting.

/// <summary>
/// Configuration class that fetches the connection string.
/// </summary>
public class UntestableClass
{
private readonly string connectionString;

private readonly string connectionStringKey = "LocalConnectionString";

/// <summary>
/// Initializes a new instance of the <see cref="UntestableClass"/> class.
/// </summary>
public UntestableClass()
{
connectionString = ConfigurationManager.AppSettings[connectionStringKey];
}

public string GetConnectionString()
{
if (string.IsNullOrWhiteSpace(connectionString))
{
throw new ArgumentNullException("The connection-string section was not found in the configuration.");
}

return this.connectionString;
}
}

This class can be tested but not without using the App.Config file. You can handle this situation by writing custom Appsetting reader that uses the ConfigurationManager or can be injected via dependency injection. But I don't want to refactor my code.

So let's go and write a test class for this. I'm using Nunit framework for writing the tests here.

 

[TestFixture]
public class UntestableClassTests
{
[Test]
public void GetConfiguration_InvalidConfigurationFound_ThrowsException()
{
UntestableClass objUnderTest = new UntestableClass(); // Here this will gonna use the actual configuration manager

var result = objUnderTest.GetConnectionString();

Assert.IsNotEmpty(result);
}
}

This is what our test would look like but I don't want to use the Real configuration manager class so let's deal with it. If you look at your references in the Test project. Right click on the referenced assembly and choose "Add Fakes Assembly".


image
image
This will add few thing in the project. One is Fakes Folder and an xml file containing the information about the faked assembly and a new reference for the faked Assembly of target.

Note: You don't have to checkin the generated assembly because they can be generated during build easily and with no extra costs.

Now we have the all the classes faked in the assembly. So I can use a faked one i.e. ConfiguationManager. This what our new tests would look like:
SNAGHTML5f0cc2a6
you might have notices few thing in this new test here. The actual class is now replaced with Shim class. You can read about the Shims and Stubs in details on MSDN.
Now if let's try to run this code:
image
So you see the test have failed. And we've got the generous message from the VS test runner. Let's modify the code as per the VS wanted.


[TestFixture]
public class UntestableClassTests
{
[Test]
public void GetConfiguration_InvalidConfigurationFound_ThrowsException()
{
using (ShimsContext.Create())
{
var appsettings = new NameValueCollection();
appsettings.Add("LocalConnectionString", "/*** Some connection string ***/");

// Changing the default behavior appsetting and put our custom behavior
ShimConfigurationManager.AppSettingsGet = () => appsettings;

// Here this will now use the faked configuration manager
UntestableClass objUnderTest = new UntestableClass();

var result = objUnderTest.GetConnectionString();

Assert.IsNotEmpty(result);
}
}
}

Running the code will result in -
image
Note: Other test runner might throw another exception e.g. The Test runner available via Resharper will fail with below message.
Microsoft.QualityTools.Testing.Fakes.UnitTestIsolation.UnitTestIsolationException : UnitTestIsolation instrumentation failed to initialize. Please restart Visual Studio and rerun this test
Reason: This happened because the executing assembly needs to be modified and needs to be instrumented for placing your custom behavior. So any test runner who have the permission to instrument the assembly can be used. I used VS test runner in this demo.


Hope you enjoyed this. I'll be discussing the Shims and Stubs in details in next post. Keep tune in.





Image courtesy: (themetapicture.com, wackybuttons.com)