Refactoring Static Config Access

Refactoring Static Config Access

A common but insidious dependency in many .NET applications is their use of configuration, including appSettings. The main issue is static access to config for values that affect the behavior of the application (values that are simply displayed, or used for things like email servers or addresses, aren’t usually an issue). Imagine you have some code like this:

Imagine this method is used to gather information about a customer before accepting their order. One option that may or may not be configured for the product is to use a geolocation service. If it’s enabled in configuration, it should be called (and its results should be used by the method); otherwise it should be ignored. Now suppose you’d like to be able to test that this method behaves correctly, for both possible settings of the useGeolocation value (and it would probably be good to test that it works correctly when the value isn’t set, too). This is difficult because test projects, while they can have an app.config file, they can only have one such file. So if your test project defines the UseGeolocation value in its app.config, that’s the value all of your tests will use. The static access to configuration settings is an example of the static cling anti-pattern (or code smell).

To be able to refactor away from this design, allowing for better testability, the first step is to replace the static configuration access with an interface:

Next, implement the interface with the default behavior, which is to read it from configuration. It’s fine to have the static access in this instance type, since the interface provides a means of replacing the implementation for client code that uses it.

In this case, I’m only reading the value the first time and storing it in a static field, and then returning the static field from the property.

Now, in the original code, you can replace access to configuration with use of the interface and its property:

At this point, writing tests that ensure CheckoutService does the right thing based on configured settings is trivial – simply pass in a new instance of an ICheckoutConfig (using a fake or a mock) with the appropriate value set. You can also, separately, test that the CheckoutConfig class does the right thing. In addition to making the code more testable, the low-level “how do I safely read a value from a configuration file, cast it, and do the right thing when it’s not there” logic is no longer in the CheckoutService type. It can remain at a higher level of abstraction, and be more focused on its problem domain (rather than plumbing code). I cover the static cling code smell, and many others, in my Refactoring Fundamentals course, if you’re interested in learning more about ways to improve your code and eliminate technical debt.

  • Hi,

    In cases when there is just a single behavioral parameter to the service, what about passing in bare boolean parameter to the service? Reading from config file could be then done in composition root. It would be also easier to test if needed. When configuration becomes more complex – maybe then it’s time to refactor to more complex data structure to be passed in. If composition of the ICheckoutConfig is moved there – then any access to external configuration sources are localized in single logical application area – composition root. Wouldn’t it make easier to refactor to even another config source if needed?

    Also, just a note – when reusing the same key/value list for various configuration keys (appSettings) – we used to prefix config keys with some functional area name and make use of nameof() expression – resulting in keys like “Checkout:UseGeolocation”. Makes it easier to find references and more importantly – refactor with IDE help.