CodeBetter.Com
CodeBetter.Com
RSS 2.0 via Feedburner
           Do you Twitter? Follow us @CodeBetter

Kyle Baley - The Coding Hillbilly

"We are stuck with technology when what we really want is just stuff that works" -- Douglas Adams

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



Comments

Kyle Baley said:

@Jay: Sure, I can update the code that I posted here: codebetter.com/.../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: haacked.com/.../writing-unit-tests-for-controller-actions.aspx

As well as Jeff Palermo's CodeCampServer: code.google.com/.../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.

# February 13, 2008 4:26 PM

Leave a Comment

(required)  
(optional)
(required)  

Enter the numbers above:
Add
Check out Devlicio.us!

Our Sponsors