How to Change your App.Config Location file at Run Time
This is one of those pieces of functionality which I have been in search of for a long time. When writing integration tests and other automated tests, you frequently want to test with different application configurations. Unfortunately, switching App.Config files on the fly is not something that the .Net framework supports. There are some alternative approaches to this problem which rely on dependency injection or using AppDomains. Depending on what you are trying to accomplish, these are not always simple or satisfactory techniques. This is especially the case when performing integration tests with third party components that rely on app.config settings and you want to test out alternatives of those settings.
But today, I found this great solution provided by Daniel Hilgarth on StackOverflow. It is something of a hack in that it relies on mucking with the internals of how AppDomains work. I don’t think I would recommend it for production code, but for automated testing, its just what the doctor ordered.
Here is my slightly modified version of the code, as well as a usage example. Please take a look at the example in the article as well, you may like it better.
using System; using System.Configuration; using System.Linq; using System.Reflection; public class AlternateAppConfig : IDisposable { private readonly string oldConfig = AppDomain.CurrentDomain.GetData("APP_CONFIG_FILE").ToString(); private bool disposedValue; public AlternateAppConfig(string path) { AppDomain.CurrentDomain.SetData("APP_CONFIG_FILE", path); ResetConfigMechanism(); } public void Dispose() { if (!disposedValue) { AppDomain.CurrentDomain.SetData("APP_CONFIG_FILE",oldConfig); ResetConfigMechanism(); disposedValue = true; } GC.SuppressFinalize(this); } private static void ResetConfigMechanism() { typeof(ConfigurationManager) .GetField("s_initState", BindingFlags.NonPublic | BindingFlags.Static) .SetValue(null, 0); typeof(ConfigurationManager) .GetField("s_configSystem", BindingFlags.NonPublic | BindingFlags.Static) .SetValue(null, null); typeof(ConfigurationManager) .Assembly.GetTypes() .Where(x => x.FullName == "System.Configuration.ClientConfigPaths") .First() .GetField("s_current", BindingFlags.NonPublic | BindingFlags.Static) .SetValue(null, null); } } |
And the usage:
[Test] public void Application_should_behave_differently_with_alternate_config() { using (new AlternateAppConfig(@"Path\to\Alternate.App.Config"))) { //Your test goes here } } //The original configuration is restored upon disposal of the alternate. |
Happy testing!
December 7th, 2012 at 5:48 pm
Just what I was looking for. Strange that the configuration system isn’t more object-oriented, though, which might make loading and adding configuration files more intuitive and unit testing MUCH easier. Instead, we get a bunch of static methods.