I want to write a unit test that verifies my route registration and ControllerFactory so that given a specific URL, a specific controller will be created. Something like this:
Assert.UrlMapsToController("~/Home/Index",typeof(HomeController));
I've modified code taken from the book "Pro ASP.NET MVC 3 Framework", and it seems it would be perfect except that the ControllerFactory.CreateController() call throws an InvalidOperationException and says This method cannot be called during the application's pre-start initialization stage.
So then I downloaded the MVC source code and debugged into it, looking for the source of the problem. It originates from the ControllerFactory looking for all referenced assemblies - so that it can locate potential controllers. Somewhere in the CreateController call-stack, the specific trouble-maker call is this:
internal sealed class BuildManagerWrapper : IBuildManager {
//...
ICollection IBuildManager.GetReferencedAssemblies() {
// This bails with InvalidOperationException with the message
// "This method cannot be called during the application's pre-start
// initialization stage."
return BuildManager.GetReferencedAssemblies();
}
//...
}
I found a SO commentary on this. I still wonder if there is something that can be manually initialized to make the above code happy. Anyone?
But in the absence of that...I can't help notice that the invocation comes from an implementation of IBuildManager. I explored the possibility of injecting my own IBuildManager, but I ran into the following problems:
- IBuildManager is marked
internal
, so I need some other authorized derivation from it. It turns out that the assemblySystem.Web.Mvc.Test
has a class calledMockBuildManager
, designed for test scenarios, which is perfect!!! This leads to the second problem. - The MVC distributable, near as I can tell, does not come with the System.Web.Mvc.Test assembly (DOH!).
- Even if the MVC distributable did come with the System.Web.Mvc.Test assembly, having an instance of
MockBuildManager
is only half the solution. It is also necessary to feed that instance into theDefaultControllerFactory
. Unfortunately the property setter to accomplish this is also markedinternal
(DOH!).
In short, unless I find another way to "initialize" the MVC framework, my options now are to either:
- COMPLETELY duplicate the source code for DefaultControllerFactory and its dependencies, so that I can bypass the original
GetReferencedAssemblies()
issue. (ugh!) - COMPLETELY replace the MVC distributable with my own build of MVC, based on the MVC source code - with just a couple
internal
modifiers removed. (double ugh!)
Incidentally, I know that the MvcContrib "TestHelper" has the appearance of accomplishing my goal, but I think it is merely using reflection to find the controller - rather than using the actual IControllerFactory to retrieve a controller type / instance.
A big reason why I want this test capability is that I have made a custom controller factory, based on DefaultControllerFactory, whose behavior I want to verify.