Sponsored By Aspose - File Format APIs for .NET

Aspose are the market leader of .NET APIs for file business formats – natively work with DOCX, XLSX, PPT, PDF, MSG, MPP, images formats and many more!

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

This entry was posted in ASP.NET MVC, Mocking. Bookmark the permalink. Follow any comments here with the RSS feed for this post.
  • http://codebetter.com/blogs/kyle.baley Kyle Baley

    @Jay: Sure, I can update the code that I posted here: http://codebetter.com/blogs/kyle.baley/archive/2008/01/25/west-palm-beach-and-the-fluent-urls.aspx

    Send me an e-mail and I’ll let you know when I updated it. I don’t really want to create a separate post about it. I pad this thing enough as it is.

    But the short answer is that I don’t create actual controllers in the tests. I’m using test-specific subclasses based on Phil Haack’s post: http://haacked.com/archive/2007/12/09/writing-unit-tests-for-controller-actions.aspx

    As well as Jeff Palermo’s CodeCampServer: http://code.google.com/p/codecampserver/

    By overriding RenderView in the subclass, I don’t have to actually call RenderView in the base class. Love it or hate it, I haven’t seen or discovered a better way to test controllers just yet without a whole lotta setup code.

    One thing that showed promise before I made this change was to create stubs manually for ControllerContext and TempData and register them in the AMC *before* requesting my controller. That way, when it could resolve those two properties when it came ’round to them. The yucky part about that is that in order to create a ControllerContext, you need a Controller. And the reason you’re creating the ControllerContext is so that the AMC can create the Controller. Nifty little Catch-22.

    In any case, that won’t work now that my changes have been added to the trunk because it will no longer try to resolve those two properties.

    Bit of a bummer living on the edge but hopefully, this will get easier as time passes.

  • Jay

    Can you post a full sample solution showing how you are mocking your controllers? I am struggling to get this working, and I am stuck on the controller’s TempData property. When RenderView is called on the controller, it constructs the ViewContext using the controller’s TempData. If that property is null, the ViewContext construction throws an exception. Since TempData is a readonly property, I don’t know how I can set it so it is not null. Any help would be greatly appreciated.