Friday, November 29, 2013

Testing Static Classes

Sometimes when you're writing unit tests, you have a need to update what someone else wrote quite some time ago.  Sometimes (usually) there are very few to no unit tests on that existing code.  This creates a couple of problems for you in your unit testing, one of which is the fact that their code may not be architected to properly accommodate your testing.  A good example of this is when someone in the past has written a static "helper" class that is used in many places.  Now you have to use that static class and your testing will depend on the values that may or may not exist.  It's messy.  Fortunately, we have a way to solve this problem using the .NET testing framework known as Shims.

I'm going to do everything else in here with commented code instead of text.  It's just easier.

public static Dictionary<stringUrlTarget> _urlTargetLookup = new Dictionary<stringUrlTarget>();

public static string BuildUrl(string destination, string displayText, string target)
{
// check whether the lookup dictionary is null or empty
       if (_urlTargetLookup == null || !_urlTargetLookup.Any())
       {
       // if it is null or empty, populate it from the database
       // this is where our unit testing problem comes into play
       // we don't want to rely on the database results for our tests to pass
       // we're assuming that the process of getting the values from the
       // database is not conducive to mocking (there are real cases
       // where this happens)
       _urlTargetLookup = GetValuesFromDatabase();
       }

// lookup the target in the dictionary and get the enum value
       var targetString = _urlTargetLookup[target].ToString();

       // build the url
       var urlBuilder = new StringBuilder();

       urlBuilder.Append(");
       urlBuilder.Append(destination);
       urlBuilder.Append("\" target=\"");
       urlBuilder.Append(targetString);
       urlBuilder.Append("\">");
       urlBuilder.Append(displayText);
       urlBuilder.Append("
");

       // return the url
       return urlBuilder.ToString();
}


public List<Endpoint> GetEndpoints()
{
var endpoints = new List<Endpoint>
       {
              new Endpoint
              {
                     Name = "Google",
                     Url = UrlHelper.BuildUrl("http://www.google.com""Google""parent")
},
              new Endpoint
              {
                     Name = "Yahoo!",
                     Url = UrlHelper.BuildUrl("http://www.yahoo.com""Yahoo!""self")
},
              new Endpoint
              {
                     Name = "Bing",
                     Url = UrlHelper.BuildUrl("http://www.bing.com""A Better Search Engine""Blank")
}
};

       return endpoints;
}


[TestMethod]
public void GetEndpointsShouldReturnThreeValidUrls()
{
// arrange
       var resources = new Resources();

       // create the ShimsContext in order to use Shims later
       using (ShimsContext.Create())
       {
// UrlHelper is a static class that contains a public static field we need to set in order to mimic a dependency injection
              // create an "instance" of this static class and use this new instance for testing
              var staticType = typeof (UrlHelper);
              ConstructorInfo ci = staticType.TypeInitializer;
              var parameters = new object[0];
              ci.Invoke(null, parameters);

              // create what amounts to a mocked Dictionary object
              var endpointMapping = new Dictionary<stringUrlTarget>
              {
                     {"_parent"UrlTarget._parent},
                     {"parent"UrlTarget._parent},
                     {"_self"UrlTarget._self},
                     {"self"UrlTarget._self},
                     {"_blank"UrlTarget._blank},
                     {"Blank"UrlTarget._blank},
                     {"blank"UrlTarget._blank},
                     {"_top"UrlTarget._top},
                     {"top"UrlTarget._top}
};

// use reflection to set the public static field in our instance of the static class so we can be certain what it will contain when we test
              var dictionaryField = staticType.GetField("_urlTargetLookup");
              dictionaryField.SetValue(staticType, endpointMapping);

              // act
              var resourcesResults = resources.GetEndpoints();

// assert
              Assert.AreEqual(3, resourcesResults.Count);
              Assert.AreEqual("Google", resourcesResults[0].Url);
              Assert.AreEqual("Yahoo!", resourcesResults[1].Url);
              Assert.AreEqual("A Better Search Engine", resourcesResults[2].Url);
}
}


So there you have it.  You can test a static class without relying on information from the database.  One important item to note is that when I left my initialization of _urlTargetLookup as null, the test failed on the staticType.TypeInitializer line because the TypeInitializer did not exist.  As long as the _urlTargetLookup is initialized to be a new instance of a Dictionary, everything works as expected.

No comments:

Post a Comment