Microsoft Fakes; Testing the Untestable Code

I have always been a great fan of the TDD approach of coding. But recently, I've ran into the situation of where the code was not testable. Before we go into further details I would like to make a disclaimer.

This is not an alien 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 the preceding agreement then I'm sure you'll enjoy this reading.

We will write some testable code to make the external dependencies injectable via dependency 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 dependencies that you can't fake. So you wrap the dependency in one of your own interfaceswhile  implementing the real deal. But this is too much code to write for removing that thing.
  3. You need to test the Legacy code that is either "Restricted" for refactoring or the "Refactoring" will cost you a lot and as the stakeholders you don't want to put that much effort into it.

As mentioned above, you might have enough reasons to become stuck and think what should I do now?

Let's get you some hope for life. Let's see the new Visual Studio 2012 Fakes feature available in the Premium and above versions only. That's enough of an explanation. I won't dive deeply into how it is relevant blah blah,. but this would be your plan B for survival. So let's use a sample of code and find the problem along 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 a custom Appsetting reader that uses the ConfigurationManager or can be inject it using a 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 the 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. Look at your references in the Test project. Right-click on the referenced assembly and choose "Add Fakes Assembly".

Add Fakes Assembly

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

Note: You don't need to check in the generated assembly because they can be generated during the build easily and at no extra cost.

Now we have all the classes faked in the assembly. So I can use a faked one, in other words ConfiguationManager. This is what our new tests would look like:

ConfiguationManager

You might have noticed a few things in this new test here. The actual class is now replaced with a Shim class. You can read about the Shims and Stubs in detail in the MSDN.

Now let's try to run this code:

run this code

So you see the test has failed. And we've got the generous message from the Visual Studio test runner. Let's modify the code as Visual Studio wants.

[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:

result

Note: Another test runner might throw another exception, for example the test runner available via Resharper will fail with the following 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 with the permission to instrument the assembly can be used. I used the Visual Studio test runner in this demo.

I hope you enjoyed this. I'll be explaining the Shims and Stubs in details in the next article. Remain tuned in.

Up Next
    Ebook Download
    View all
    Learn
    View all