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.

Sunday, November 24, 2013

Sometimes You Gotta Fake It

In .NET 4.0 Microsoft gave us a couple of new testing tools called Shims and Fakes.  I'm not going to profess to be an expert on those topics, nor on unit testing in general.  However, I have found there to be a dearth of information available to actually utilize these new tools to do unit testing.  I've written up a nice little blurb on how to do it and I'm going to post it here for posterity.

First we must create a ShimsContext within which we can run the tests.  ShimsContext implements IDisposable so we can do this by utilizing the using statement:

// create the ShimsContext in order to use Shims later
using (ShimsContext.Create())
{
}


Within the ShimsContext we can use Shims and Fakes to specify the exact data we want to receive when we use static methods.  The first method here specifies that when DateTime.Now is called by the code under test, it should be detoured to use “11/15/2013 9:18:13”:
// create the ShimsContext in order to use Shims later
using (ShimsContext.Create())
{
// create the Shim for DateTime.Now in order to always have a specific value when DateTime.Now is called
       System.Fakes.ShimDateTime.NowGet = () => new DateTime(2013, 11, 15, 9, 18, 13);
}

The next thing we do is create a Fake for Session.  This way we can specify an exact value for each key requested from Session.  Since we can specify a value for each key, we have full control over the session and which values will be returned, including potentially returning null so we can perform negative testing.  Here we want to specify that "12345" should be returned when the code requests "UserID" from the session and "987654" when "LoginKey" is requested:


using (ShimsContext.Create())
{
<…snip…>

// create the Fake for Session
var session = new System.Web.SessionState.Fakes.ShimHttpSessionState
{
ItemGetString = key =>
{
// specify each key that will be requested by the code under test
switch (key)
{
case "UserID":
return "123456";
  case "LoginKey":
return "987654";
default:
return null;
}
}
};
}


Now that we’ve created the Session Fake we need to create the Context Fake.  In this case I want to specify that when HttpContext.Items is called by the code under test, an empty Dictionary<string, object>() is returned:

// create the ShimsContext in order to use Shims later
using (ShimsContext.Create())
{
<…snip…>

// create the Shim for HttpContext
var context = new System.Web.Fakes.ShimHttpContext
{
ItemsGet = () => new Dictionary<string, object>()
};
}


Once the Fakes are both created, we must tell the Shims to use those objects instead of the standard objects.  These two lines specify that when HttpContext.Current is referenced we should instead substitute the Fake context we created earlier and when HttpContext.Current.Session is referenced we should instead substitute the Fake session we created earlier:

// create the ShimsContext in order to use Shims later
using (ShimsContext.Create())
{
<…snip…>

// specify that when the current context is requested we return the shimmed context we created
System.Web.Fakes.ShimHttpContext.CurrentGet = () => context;
// specify that when the current session is requested we return the shimmed session we created
System.Web.Fakes.ShimHttpContext.AllInstances.SessionGet = o => session;
}


That's pretty much it for now.  Shims and Fakes are incredibly useful, especially when you need to test a method that uses values from Session to perform differently.  I'll post again soon (hopefully) on how to instantiate a static class for testing purposes.