Property.Settings.Default makes it hard to unit test any method that uses it

Storing your application and user settings should be a solved problem. After all, we have done it since the dawn of software development. Yet, the question “What is the best way to store settings?” keeps cropping up. The answers vary widely and quite often spark religious wars.

As interested as I am in why people are so invested in whatever they use, it doesn’t help me decide one way or the other.

Does it really matter? It’s not like it is a life and death decision.

Just use what is provided by your development environment and be done with this already!

Using Visual Studio, that would mean you opt for using Properties.Settings.Default.

The reasons to do so are obvious:

  • It’s the recommended way to store settings for Windows Forms Applications by 283 upvotes on StackOverflow [1], as well as by CodeProject [2] and of course MSDN [3] itself.
  • The Visual Studio Settings designer generated a settings class that allows you to work with your settings in a type safe manner. Much better than the ToString / FromString hell that is ConfigurationManager.AppSettings.
  • It’s quick and convenient. No coding required.

All you need is something like:

    class SettingsMineField
    {
        public Int MethodReadingSomeSetting()
        {
            // <snip>
            string LastFolder = Properties.Settings.Default.LastUsedFolder;
            // <snip>
            return Whatever;
        }

        public void MethodWritingSomeSetting(string newValue)
        {
            // <snip>
            Properties.Settings.Default.LastUsedFolder = newValue;
            Properties.Settings.Default.Save();
            // <snip>
        }
    }

Sounds good, hah?

But… what about testing that those methods do what they are supposed to do and keep doing so in future as well?

Using Properties.Settings.Default throughout your code:

  • You bind yourself hand and foot to whatever storage mechanism you hook up (by default the app.config and user.config files).
  • You turn every function that uses Properties.Settings.Default into something that can only be tested through slow integration tests instead of quick, fast executing and more focused unit tests.
  • You will have a hard time to arrange your tests properly as it turns out that is a lot of work, even somewhat of a hack, to get Properties.Settings.Default to work with test content instead of the actual application and user configuration files. [4], [5]

In short, you dig yourself into a hole when it comes to testing your application. If you care about unit testing your code at all, the reasons not to use Properties.Settings.Default are obvious as well.

What to do? How do you get the benefits of using Properties.Settings.Default without the drawbacks?

Dependency injection comes to mind. Which could look something like this:

    class HappyCamper
    {
        Properties.Settings _Settings;

        public HappyCamper(Properties.Settings useThisSettingsInstance)
        {
            _Settings = useThisSettingsInstance;
        }

        public Int MethodReadingSomeSetting()
        {
            // <snip>
            string LastFolder = _Settings.LastUsedFolder;
            // <snip>
            return Whatever;
        }

        public void MethodWritingSomeSetting(string newValue)
        {
            // <snip>
            _Settings.LastUsedFolder = newValue;
            _Settings.Save();
            // <snip>
        }
    }

However, as Properties.Settings is a sealed class, there is no way to circumvent the standard behavior of that class. And, where the resources mentioned above sought to force the configuration management framework to use files with test content instead of the actual files, you want to go one better.

You want your actual program to reap the benefits of the built-in configuration management, and you want your tests independent of the app.config and user.config files (or any other storage mechanism that the built-in configuration management provides) to make them part of your fast executing unit test suite.

So… you need to sever the umbilical cord between the code using settings and the configuration files. If that sounds like breaking a dependency to you: Yay! That is exactly what it is.

If you can’t change or easily influence the (default) behavior of some built-in class, the answer is to insulate your code from the trouble by introducing a layer over which you do have total control. Whether you do so by inheritance or composition doesn’t matter. Either method will provide you with the seams you need to fake the settings used by the code under test.

Inheritance has the advantage of making all standard behavior immediately accessible with very little extra code. Composition takes a bit more work: you need to “replicate” the properties and methods the rest of your code needs and patch them through to the wrapped class. Despite that, I generally prefer composition. It ensures that the properties and methods of and the types used by the wrapped class do not leak into the rest of my code.

As the Properties.Settings class is sealed, inheritance is out.

Wrapping Properties.Settings for our example above:

    class HappySettings
    {
        Properties.Settings _Settings;

        public HappySettings()
        {
            _Settings = Properties.Settings.Default;
        }

        public string LastUsedFolder 
        { 
            get { return _Settings.LastUsedFolder; } 
            set { _Settings.LastUsedFolder = value; } 
        }
        public void Save() { _Settings.Save(); }
    }

Which allows the SettingsMineField class that we started with to become:

    class EvenHappierCamper
    {
        HappySettings _Settings;

        public EvenHappierCamper(HappySettings useThisSettingsInstance)
        {
            _Settings = useThisSettingsInstance;
        }

        public Int MethodReadingSomeSetting()
        {
            // <snip>
            string LastFolder = _Settings.LastUsedFolder;
            // <snip>
            return Whatever;
        }

        public void MethodWritingSomeSetting(string newValue)
        {
            // <snip>
            _Settings.LastUsedFolder = newValue;
            _Settings.Save();
            // <snip>
        }
    }

Which in turn allows your unit tests to provide EvenHappierCamper with any instance of a HappySettings test descendant that you wish, leaving us with:

Mission accomplished!

You get the benefits of using Properties.Settings.Default without the drawbacks!

Enjoy!

What is the one code practice that has you foaming at the mouth because it cramps your unit testing style? I’d love hearing from you by email or in the comments.

Notes

[1] Best practice to save application settings in a Windows Forms Application

[2] Windows Forms User Settings in C#

[3] Application Settings Overview

[4] How do I select a .Net application configuration file from a command line parameter?

[5] Change default app.config at runtime

Posted in Software Design
Tags: , , , ,
2 comments on “Property.Settings.Default makes it hard to unit test any method that uses it
  1. I’m writing dependency injected wrappers as well. Usually the interface is named IAppConfig and the class AppConfig (:

    It also allows for some more intelligent wrapping of data types present in the app.config files and what you use on the C# side.

    • Marjan Venema says:

      Oh yes, using an interface would be even better. Didn’t want to muddy the waters. Keeping things focused is hard enough as it is 🙂

Leave a Reply

Your email address will not be published. Required fields are marked *

*

Show Buttons
Hide Buttons