I use settings file left and right - not everything I do requires DI - and despite what some would have you believe, not everything you do requires it either. A particular annoyance of mine is seeing '0%' code coverage (or very low numbers) in
Visual Studio when I run my tests which throws my overall percentage (which is generally 100% everywhere else) down. This combined with the fact
that testing something that that relies on a value that comes from a 'settings' file is 'troublesome' at
best. Now many purists would believe my design is bad because I should have some layer of abstraction or use DI, blah blah blah, but I'm not a purist and I generally believe purists can't see the forest through the trees. Anyway,
I've come up with the following strategy for dealing with this annoyance and hopefully someone else out there who is not a purist will find it helpful.
The auto-generated settings file as we all know is sealed, but it's also partial,
unfortunately Microsoft doesn't add the DebuggerNonUserCode
at the class level (which IMO they should). I want my tests to have the
ability to inject values so that the code I'm testing, which will pull a
setting, can be injected with whatever value I like and thus accurately
tested. I don't want to jump through a bunch of hoops to do this
either.
Since I always have InternalsVisibleTo set for RhinoMocks and my test assembly, I can see this code, so in
my tests, I'll just set the 'setting' to whatever I want it to be.
Again some purists might argue that I should abstract things out and create
more moving parts - but this was quick (5 minutes) and easy (obviously looking at the code) and gets the job done without creating a bunch of complexity or an 'advanced' level of programming skills. My tests that use this helper are nice and clean with this and run under a 1/10th of a second. Some may say I'm exposing a 'hole' in my code because now a user with reflection could alter a setting. You're right they could. Why they would do it through reflection versus updating the config file they have; I don't know - but I'm not worried about the application falling apart because of user mis-configuring a setting through code anymore so that I am them mis-configuring through XML. The only thing that I'd suggest is that you could have a 'generic' method that would work for anything and you'd just pass in the name of the setting; but at the time of this writing I haven't refactored to that point yet.
/// <summary>
///
This object is purely for testing purposes and also helps
remove coverage analysis from
///
the Setting class that is auto generated by Visual Studio.
/// </summary>
/// <remarks>
///
The DebuggerNonUserCode attribute will need to
removed from the settings file if Visual Studio
///
regenerates the file. Visual Studio only applies this attribute to the
instance properties that wrap
///
your settings, not to the class itself or the static 'Defaut'
property. This can easily be done with a
///
Find and Replace on the desinger file. You
shouldn't be changing settings that often anyway.
///
///
Failure to remove the attribute from the designer will result in off the wall
errors in your unit tests.
///
///
If you remove the attribute here because of errors in
your unit test, then code coverage will not be
///
accurate; we normally don't care about coverage on a settings file.
/// </remarks>
[DebuggerNonUserCode()]
internal sealed partial class Settings
{
/// <summary>
///
Sets the Logger property to the specified value.
/// </summary>
/// <param
name="logger">
///
The name of the logger in Assembly, FullClassName format.
/// </param>
internal void SetLogger(string logger)
{
Properties["Logger"].DefaultValue = logger;
//Call Reload in order to submit the change,
otherwise, when setting, the setting may not stay,
//this is particularly seeting
when setting to string.Empty
Reload();
}
/// <summary>
///
Sets the LogLevel property
to the specified value.
/// </summary>
/// <param name="logLevel">
///
The value that should be entered into the LogLevel setting.
/// </param>
internal void SetLogLevel(string logLevel)
{
Properties["LogLevel"].DefaultValue = logLevel;
//Call Reload in order to submit the change,
otherwise, when setting, the setting may not stay,
//this is particularly seeting
when setting to string.Empty
Reload();
}
}