AutoMockingContainer and ASP.NET MVC: Round 2
Many thanks to honorary hillbilly, Aaron Jensen for both nudging me into helping out when I lamented on the AutoMockingContainer/ASP.NET MVC combination and for helping me through my first official patch submission to an open source project. He was very gracious with my lapse in matching formatting and spacing standards.
The end result is that you (and, more importantly, I) can now use the AutoMockingContainer to create controllers in ASP.NET MVC. Whether or not you think that's a good thing, I'm feeling mighty proud of myself either way.
The issue previously was that when the container tried to create the controller, the default behaviour was to create dynamic mocks for any properties it could. Controllers have two properties, ControllerContext and TempData, that can't be mocked without help, primarily because they don't have default constructors.
The fix was trivial in the way that Ayende would say something was trivial. That is, it wasn't trivial for me at all but it sure looks like it was in the finished code.
By default, Windsor will try to instantiate an object using the DefaultComponentActivator. One of the methods it calls is SetupProperties which, as one might expect, loops through the object's properties and resolves any that it can. And the AutoMockingContainer assumes that it can resolve any and all comers without any outside help.
The fix was to create a custom component activator that derives from DefaultComponentActivator but that overrides SetupProperties so that it does nothing:
public class AutoMockingComponentActivator : DefaultComponentActivator { public AutoMockingComponentActivator(ComponentModel model, IKernel kernel, ComponentInstanceDelegate onCreation, ComponentInstanceDelegate onDestruction) : base(model, kernel, onCreation, onDestruction) { } protected override void SetUpProperties(object instance, CreationContext context) { } }
Then, thanks to Castle's extensibility, that activator can be wired into the Kernel in the AutoMockingContainer (bolded code is new):
public void Initialize() { Kernel.AddSubSystem(SubSystemConstants.NamingKey,new AutoMockingNamingSubSystem(this)); Kernel.AddFacility("AutoMockingFacility", new AutoMockingFacility(this)); Kernel.ComponentModelCreated += Kernel_ComponentModelCreated; } void Kernel_ComponentModelCreated(Castle.Core.ComponentModel model) { model.CustomComponentActivator = typeof(AutoMockingComponentActivator); }
Retrofit some tests (oops! I mean I wrote those first) and boom-bada-billy-bing, I'm off to the races. At the expense of about four billable hours.
Kyle the Activated