2011-05-20

Moles: very close, still no cigar

If you don't know about Moles yet, you should: it allows you to redirect the implementation of almost any method or type in .NET. With it you can test what would otherwise be untestable (even assuming we're willing to refactor all code that needs refactoring, a philosophy I don't subscribe to anyway). Cool as it is, it still can't do everything.

Unlike the default test runner, if you use Moles, the application configuration file is inaccessible. You get the Moles configuration file instead, which is useless. The problem is described in this post but there's no working solution. The code posted in this thread to mole ConfigurationManager.GetSection() doesn't work, as clever an idea as that is -- you'll run into trouble reading connection strings and the "solution" means you practically need to reimplement it from scratch, with knowledge of how the configuration system works internally (trust me, this is not pretty).

This is a particular shame because Moles is perfect for white-box testing without the need for extensive refactoring first -- ideally you want to leave the original code as-is and mole any external dependencies to make the tests reproducible. The prospect of, say, refactoring all database code used in an application because it wasn't originally designed with a separate data layer in mind is not only unattractive, it can lead to its own problems with maintainability.

Configuration settings are a very basic external dependency, so it's a real shame the current release of Moles doesn't read them properly. It's not just Moles that is to blame, but also the design and implementation of ConfigurationManager. You'd think it should be a simple matter to retarget the configuration at run-time (Moles or no), but it's not. Although the Configuration class allows you to read any configuration file, its semantics are not the same as ConfigurationManager. Configuration.GetSection() and ConfigurationManager.GetSection() are subtly different.

If, like me, you don't use custom configuration sections but only access .AppSettings and .ConnectionStrings, you can easily mole just those two properties. Of course, then I ran into another Moles limitation that shows just how pointless the whole exercise was: Moles redirection doesn't work inside static constructors -- something the manual neglects to mention -- and sure enough, the code in question reads some connection strings into static fields. Moles does have a feature whereby you can erase static constructors, which allows you to work around issues like these at the cost of rewriting the initialization logic.

In this case, you'd really want to rewrite the offending code. Static constructors that do anything other than some trivial local initialization are a bad idea, because the order in which they're executed is undefined and handling errors that occur in static constructors is essentially impossible (in theory, you can handle TypeInitializationException, in practice, type initialization happens at unpredictable moments). Static constructors should not fail -- and accessing a connection string can fail.

Of course, the offending code in question was located in a library and rewriting it would break the interface... nobody said programming was easy.

No comments: