3

I have been spinning my wheels now for awhile trying to figure out how I can possibly unit test the following code. At first I was going to use Moq to mock everything, but it doesn't include the ability to mock sealed classes. I know I need to abstract out the calls to the implementation (Configuration) using an interface? but I can't seem to make it all work right.

The code can be altered but I would prefer to keep the methods static, unless you can present a compelling reason not to. You can add interfaces or create whatever seams are needed. Also, GetConnStringByName() can be refactored to return the relevant string instead of a ConnectionStringSettings.

Thoughts?

namespace Stackoverflow.Rocks
{
    /// <summary>
    /// Utility class for progmattically selecting values from the Web.config file.
    /// </summary>
    public class WebConfigStrings
    {
        //private static Configuration myConfiguration = System.Web.Configuration.WebConfigurationManager.OpenWebConfiguration(HttpContext.Current.Request.ApplicationPath);

        /// <summary>
        /// Retrieves the desired connection string value based upon the target name.
        /// </summary>
        /// <param name="connectionStringName">The target connection string referenced in the Web.Config</param>
        /// <returns>The value specified in the Web.Config by your connectionStringName</returns>
        public static ConnectionStringSettings GetConnStringByName(string connectionStringName)
        {
            Configuration rootWebConfig = System.Web.Configuration.WebConfigurationManager.OpenWebConfiguration(HttpContext.Current.Request.ApplicationPath);
            ConnectionStringSettings connString;
            connString = rootWebConfig.ConnectionStrings.ConnectionStrings[connectionStringName];
            return connString;
        }

        /// <summary>
        /// Retrieves the desired application string value based upon the target name.
        /// </summary>
        /// <param name="applicationStringName">The target application string referenced in the Web.Config</param>
        /// <returns>The value specified in the Web.Config by your applicationStringName</returns>
        public static string GetAppStringByName(string applicationStringName)
        {
            string appString = "";
            appString =  ConfigurationManager.AppSettings[applicationStringName];
            return appString;
        }

    }
}
bulltorious
  • 7,769
  • 4
  • 49
  • 78

4 Answers4

2

Refactor your classes to use the new types defined in System.Web.Abstractions. This assembly define mockable types which wrap the core (and mostly static or sealed) types that asp.net is built around. This will allow you to switch out the real runtime types with mocks in tests.

Community
  • 1
  • 1
  • Why is that necessary? The purpose of the code is to retrieve the values from the configuration file, so why not just add dummy values at the expected places in the configuration file and verify that code reads those actual values? Am I missing something? Is it because of the `HttpContext`? – Tomas Jansson Jan 07 '11 at 14:11
  • Will - while Tomas technically answered it, your answer will help me GREATLY in the future and was more along of the lines of what I thought the answer would be. Upboat for you :). – bulltorious Jan 07 '11 at 14:45
  • 1
    @Tomas @bull the problem is that you're not unit testing your code--you're integration testing your code and the standard asp.net class code. Also, how do you add "dummy" values that are correct, are incorrect, and are non-existent? You need three application settings files. By being able to mock the asp.net classes you have dependencies on, you are able to control what is returned by them. This is pretty much rudimentary unit testing procedure. Think of it this way--if this wasn't a good test design, why did Microsoft create System.Web.Abstractions? –  Jan 07 '11 at 15:22
  • @Will: I do agree to some points, and I have to add the I haven't looked at `System.Web.Abstractions`. But, if you look at what the actual code is doing it is integration with .Net-framework, so why not test it the simplest possible way. And why do you need three application settings files? I could see the point if you need to test more complicated stuff where you need more functionality out of the `HttpContext`, and `System.Web.Abstractions` would give you that. – Tomas Jansson Jan 07 '11 at 15:45
  • @Will: Just thought it through once more and I see your point with :). But I don't know the `System.Web.Abstractions` framework, but I just take your word that it is good. – Tomas Jansson Jan 07 '11 at 16:17
  • @Tomas np. BTW, the MVC framework exposes all asp.net types as abstractions, which makes testing controllers much easier. –  Jan 07 '11 at 19:27
0

If anyone is interested here's what I'm doing, I'm using a Fakes assembly for System.Web and in my using(ShimsContext.Create()) block I have:

System.Web.Configuration.Fakes.ShimWebConfigurationManager.ConnectionStringsGet = () => new ConnectionStringSettingsCollection(){ new ConnectionStringSettings("name", "fake connection string")};

Not sure if that is what you're looking for but thought it was worth mentioning.

Eric Alan Solo
  • 337
  • 3
  • 9
0

In your test project add those sections that you need to your configuration file with dummy values and verify that those are the actual values you get.

Tomas Jansson
  • 22,767
  • 13
  • 83
  • 137
  • Tomas - your idea worked...for the most part. You can't create a fake web.config, but you can create an App.config. This forced me to refactor the body of the GetConnStringByName() to return ConfigurationManager.ConnectionStrings[connectionStringName]; which I am fine with, as that code still works in my program...and is now TESTABLE. – bulltorious Jan 07 '11 at 14:42
  • Great to hear it works out for you... just keep it simple as long as it go :) – Tomas Jansson Jan 07 '11 at 14:51
0

The easiest way is to add dummy values to the configuration file. Being that your methods are static, you can't mock the class with Moq/RhinoMocks. You'd have to use TypeMock or maybe Telerik JustMock which have this capability (but also cost money).

If you want to mock the class, you have to make the class an instance or consider allowing the setting of a wrapper class as a static property that handles the configuration stuff. This way, you can use an interface for testing, provide a real class that uses the configuration file, then swap this out during testing with a fake by setting the fake to the static property.

HTH.

Brian Mains
  • 50,520
  • 35
  • 148
  • 257