Joe White's Blog Life, .NET, and cats

Intercepting constructors #.NET

.NET has some features that could be very, very cool for writing unit tests and doing test-driven development.

One area that shows a lot of promise is mock objects. For example, if I want to test my database-access code, but don't want to create a clean database every time I run my tests, I could create a mock object that isn't a real database connection but that plays one on TV — a class that knows just enough to react appropriately if the test class does what it's supposed to, perhaps by using a state machine (NMock does this). That idea is nothing new, but prior to .NET it was easy to mock an interface but hard to mock a real class. In .NET, you can mock a real class, even intercepting non-virtual methods and making GetType() pretend that you're the actual class.

The easy way to do this is with RealProxy. Just descend from it, pass the type you're mocking to the constructor, call GetTransparentProxy() and cast it to that type, and go. The transparent proxy looks like the real thing, but any calls to it are magically turned into calls to RealProxy.Invoke(), with a bunch of parameters (via an IDictionary) like method name, parameter list, etc. Even non-virtual methods can be intercepted. The only downside is that you can only mock a class if it descends from MarshalByRefObject. (You can also mock an interface if you like.)

But things get even more interesting. .NET Remoting can actually intercept constructors. Just add some options to a configuration file, and suddenly every time you call new Foo() it's actually returning a remoting proxy instead. Looks exactly like a Foo, but adds whatever custom behavior it needs (e.g., passing calls to another computer). And totally automatic. Sweet, huh? Imagine hooking that for testing. "The first time someone creates an instance of Bar, expect these parameters, and return an instance with these properties. The second time someone creates an instance of Bar, expect these parameters, and throw this exception." Very interesting possibilities indeed. And there's got to be a way to hook constructors the same way Remoting does, right?

Well, I haven't found it yet, but I'm looking. Method interception happens at a lower level than IL, so this is one case where Reflector can't help (much). If you descend from ContextBoundObject and put the right attribute on your class, you can have someone intercept your constructor; that's a possibility, but I'd rather have it also available for classes that didn't expect to have their constructors intercepted. I found an article that delves deep into the mysteries of ContextBoundObject that may have more insight. (So far I've only read half the article; I'm posting about it here to remind me to finish reading it later.)