The other day I had an issue with a custom configuration section I would like to talk you about.
My take on configuration is stick as much as possible with .settings configuration until collections, nested elements or some other advance technique is needed. It does not happen often that I need a custom section, though.
My requirements were very simple: a custom configuration element with a mandatory string attribute that must be non-empty.
But it went wrong, and to prevent you make the same mistake and waste some valuable minutes of your life scratching your head, I will tell you what went wrong with my implementation. But I will tell the story backwards for two reasons:
- to have a control scenario to check that I am not completely off
- to explain my approach to custom .Net configuration
The Configuration Class
As I always do, I wrap all my configurations behind interfaces, so that the surface of contact with the runtime system can be abstracted away.
Additionally, I facilitate testing my configuration classes by allowing getting the custom section from a different file that is not the one for the running program, so that I can test that the structure and constraints of my configuration artefacts are correct. In this case it would look something like this:
The Custom Section
For configuration I tend to move away from the “declarative style” for a couple of reasons:
- a bit more performance can be expected from the “programmatic style” as it skips reflection when initializing the classes
- the other is that it is so simple that makes the declarative model less attractive due to reason #1
Basically what you do is set up the configuration properties in the static constructor and override the properties collection for further performance. Our section would look like this:
Nothing major, just that the NeatlyNamed property is configured as mandatory.
The Custom Element
As with the section we follow the same idea of static initialization:
Again, nothing specially shocking, just that the NeatlyNamedProperty is mandatory and it has to be non-empty string.
How to check that configuration is correct without waiting to build a program, set up the configuration file with one of the cases to test, run the program and and check the results, stop the program, change the configuration file to set up another case, run the program again, check the results, …?
The answer is automated tests.
Since we took care of designing the configuration class with an extra constructor to inject a file path, we are able to change where the configuration is loaded from within the same fixture run. The trick is point to a fake assembly that has a configuration file with the case we want to check.
For instance, to check that our configuration works as it should we would use this configuration file:
That can be consumed by this test:
The only non-evident part is what the path of the fake assembly is. With NUnit the path is relative to the assembly. With other testing frameworks (MSTest, for instance) something else might be needed in order to locate the files.
Fine then, we are in disposition to tell that our custom section works as expected before running our program.
Would it tell us when the configuration is wrong? We mentioned that we had two requirements for the property: that it has to be mandatory and its value ought to be non-empty. To check the first scenario we use the following configuration file and test:
Interestingly, the configuration throws and exception when it initializes and we are able to assert on the exception message. We could have chosen a weak assertion on just the exception, or have been more specific asserting that the message contains the offending attribute name or have become as specific as we one can get and, after doing some reverse engineering of the framework code, assert on the exact message that was going to be received.
The remaining test operates in the same fashion and the code can be reviewed, as always, in
Time for my mistake. What would happen if we created another element with the same structure and same requirements (mandatory non-empty attribute)?:
And, in the same fashion as with the NeatlyNamedElement, expose the value of the property though the MyConfiguration class so that we can test it. What would be the result of the following test?
Would it succeed? The cunning reader would say “NO” or otherwise it would not be in the offender section ;-)
Reality is that the test does not pass as no exception is thrown even if the attribute is actually missing.
Why? The reason is that both the element and the attribute has the same name. Therefore, a configuration property of “poorlyNamed” name is found, even though is not a string.
Moral is: if you do not want to find weird configuration behaviours, do not name different properties with the same name, event if they are of different nature.
Enjoy your configurations.