How do I test area registration logic in MVC 3? - c#

I have simple HttpApplication class:
public class MvcApplication : HttpApplication
{
public void Application_Start()
{
// register areas
AreaRegistration.RegisterAllAreas();
// register other stuff...
}
}
My unit tests initialise HttpApplication, invoke ApplicationStart and verify application start-up behaviour.
This approach worked well until I had to integrate MVC areas. When AreaRegistration.RegisterAllAreas() is invoked by a unit test, the following exception gets thrown:
System.InvalidOperationException: This method cannot be called during the application's pre-start initialization stage.
Is there a good approach for testing area initialisation logic?

Temporary workaround:
1) In MvcApplication, expose virtual method RegisterAllAreas()
public class MvcApplication : HttpApplication
{
public void Application_Start()
{
// register areas
RegisterAllAreas();
// register other stuff...
}
public virtual void RegisterAllAreas()
{
AreaRegistration.RegisterAllAreas();
}
}
2) In specification, implement a proxy:
[Subject(typeof(MvcApplication))]
public class when_application_starts : mvc_application_spec
{
protected static MvcApplication application;
protected static bool areas_registered;
Establish context = () => application = new MvcApplicationProxy();
Because of = () => application.Application_Start();
It should_register_mvc_areas = () => areas_registered.ShouldBeTrue();
class MvcApplicationProxy : MvcApplication
{
protected override void RegisterAllAreas()
{
areas_registered = true;
}
}
}
3) Test AreaRegistration implementations individually
4) Exclude MvcApplication.RegisterAllAreas() from test coverage
I don't like this approach, but can't think of a better solution right now.
Ideas and comments are welcome…

Related

Custom Controller, not working with areas

I have a custom controller in my project because I use Structure Map as an IOC Container. When I add Datatables.mvc (library for jquery grid), and call a view from areas, the custom Controller does not work at runtime and gives me an error. When I remove the Datatables.mvc from references, the project compiles successfully and renders the view form controller as well.
public class MvcApplication : System.Web.HttpApplication
{
protected void Application_Start()
{
AreaRegistration.RegisterAllAreas();
FilterConfig.RegisterGlobalFilters(GlobalFilters.Filters);
RouteConfig.RegisterRoutes(RouteTable.Routes);
BundleConfig.RegisterBundles(BundleTable.Bundles);
setDbInitializer();
//Set current Controller factory as StructureMapControllerFactory
ControllerBuilder.Current.SetControllerFactory(new StructureMapControllerFactory());
Microsoft.AspNet.SignalR.GlobalHost.DependencyResolver = SmObjectFactory.Container.GetInstance<Microsoft.AspNet.SignalR.IDependencyResolver>();
}
private static void setDbInitializer()
{
Database.SetInitializer(new MigrateDatabaseToLatestVersion<OnlineAcademyDbContext, Configuration>());
SmObjectFactory.Container.GetInstance<IUnitOfWork>().ForceDatabaseInitialize();
}
public class StructureMapControllerFactory : DefaultControllerFactory
{
protected override IController GetControllerInstance(RequestContext requestContext, Type controllerType)
{
if (controllerType == null)
{
throw new HttpException(404, $"Resource not found : {requestContext.HttpContext.Request.Path}");
}
return SmObjectFactory.Container.GetInstance(controllerType) as Controller;
}
}
protected void Application_EndRequest(object sender, EventArgs e)
{
HttpContextLifecycle.DisposeAndClearAll();
}
}
and this is the error when i called a view from areas and Datatable.mvc has been add to references:
System.Web.HttpException: Resource not found : url of area...
ControllerType is null
thanks all.
well. problem has been solved. the solution is change the computer. i think the iis or Os got confuse cause

MVC global exceptions

I am coding an MVC 5 internet application, and I have a question in regards to handling exceptions globally.
I have my Application_Error setup in my global.asax file. This caters to errors such as 404 HttpExceptions.
How can I send all errors that occur in a controller to the Application_Error function? An example is the following exception:
System.Web.HttpRequestValidationException: A potentially dangerous
Request.Form value was detected from the client (name="").
I have written a OnException(ExceptionContext filterContext) for my controller, but am not sure on how to get the Application_Error function to handle these errors. Do I need to pass the exception from the OnException function, or is this the wrong approach?
Thanks in advance.
You can create a global filter by adding the following class to your App_Start folder:-
public class FilterConfig
{
public static void RegisterGlobalFilters(GlobalFilterCollection filters)
{
filters.Add(new HandleErrorAttribute());
}
}
HandleErrorAttribute can be replaced with your own custom Exception Filter.
All you then need to do is make sure you add the following line of code to the App_Start method of your Gloabal.asax :-
public class MvcApplication : System.Web.HttpApplication
{
protected void Application_Start()
{
//AreaRegistration.RegisterAllAreas();
//RouteConfig.RegisterRoutes(RouteTable.Routes);
FilterConfig.RegisterGlobalFilters(GlobalFilters.Filters);
}
}
Hope this helps.
I'm using some kind of http-module which gives me exactly what you are asking for:
public class MyModule : IHttpModule {
public void Init(HttpApplication context) {
context.Error += OnRequestError;
}
private void OnRequestError(object sender, EventArgs e) {
var context = ((HttpApplication)sender).Context;
var error = context.Error;
if (error == null)
return;
var errorType = error.GetType();
if (errorType == typeof(HttpException))
// do something
// this is what you are looking for
if (errorType = typeof(HttpRequestValidationException))
// do something, whatever you want
// works for me, so should work to you too
}
}
To get the module to work, you can use web.config or DynamicModuleHelper:
Install Microsoft.Web.Infrastructure and WebActivatorEx via nuget
Add a Bootstrapper class to your project
Register module at PreApplicationStartMethod
Sample:
// File: Bootstrapper.cs (contains class Bootstrapper)
using Microsoft.Web.Infrastructure.DynamicModuleHelper;
using WebActivatorEx;
using WhatEver.It.Is;
[assembly: PreApplicationStartMethod(typeof(Bootstrapper), "Bootstrap")]
namespace WhatEver.It.Is {
public class Bootstrapper {
public static void Bootstrap() {
// Do what do you need just before the application get started
// like registering modules, etc...
DynamicModuleUtility.RegisterModule(typeof(MyModule));
}
}
}

Using an IOC container as dependency resolver for MVC5 throws ' cannot create an instance of an interface ' error

I am trying to simply use an IOC container ( ninject at present ) as the dependency resolver for MVC5.
This used to work fine in MVC4, visual studio 2012, but now with VS2013 and MVC5 , I just can't get the resolver to inject a dependency in my controller.This isn't specific to ninject, I have tried SimpleInjector and Unity too - same error
I just want to be able to inject this class in my home controller.
public interface ITest
{
void dummyMethod();
}
public class Test : ITest
{
public void dummyMethod()
{
};
}
This is the dependency resolver
public class NinjectDependencyResolver : IDependencyResolver
{
private IKernel kernel;
public NinjectDependencyResolver()
{
kernel = new StandardKernel();
AddBindings();
}
public object GetService(Type serviceType)
{
return kernel.TryGet(serviceType);
}
public IEnumerable<object> GetServices(Type serviceType)
{
return kernel.GetAll(serviceType);
}
private void AddBindings()
{
kernel.Bind<ITest>().To<Test>();
}
}
This is the global.asax.cs
public class MvcApplication : System.Web.HttpApplication
{
protected void Application_Start()
{
AreaRegistration.RegisterAllAreas();
DependencyResolver.SetResolver(new NinjectDependencyResolver());
FilterConfig.RegisterGlobalFilters(GlobalFilters.Filters);
RouteConfig.RegisterRoutes(RouteTable.Routes);
BundleConfig.RegisterBundles(BundleTable.Bundles);
}
}
and this is my HomeController
public class HomeController : Controller
{
public ActionResult Index(ITest test)
{
return View();
}
}
but when I run this I keep getting
Server Error in '/' Application.
Cannot create an instance of an interface.
I have also tried creating a brand new project ( MVC 5) - same error
I have tried MVC5, and then also upgrading to 5.2.2. Same error
Any help greatly appreciated. I think that the resolver is never being called for some reason, even though if I put a breakpoint at
kernel.Bind<ITest>().To<Test>();
it does stop there.... No idea what is happening :(
Normally you cannot inject parameters into your action methods.
You need to inject your dependencies in your constroller's constructor :
public class HomeController : Controller
{
private readonly ITest test;
public HomeController(ITest test)
{
this.test = this;
}
public ActionResult Index()
{
//use test here
return View();
}
}

Using Decorator Pattern with an ASP.NET (non-MVC) page

I've only recently started to develop in ASP.NET, though I've been using C# for several years now. My first approach to ASP.NET was using MVC 4, but now I find myself working on a project that uses plain ASP.NET pages. I'm at a loss as to how the general framework works.
Specifically, I have no idea how to implement the Decorator Pattern in an ASP.NET page. I have a Product.aspx page and I have to add a feature to it. I thought that Decorator Pattern would be best based on the task requirements, and I immediately figured out how I would use it in MVC, since the actual logic that is executed lies in the Controller Action: there I would instantiate my decorator object.
But I have no idea how to do it in ASP.NET. As far as I can see, when the browser requests Product.aspx "something" creates an object of class Product (derived from Page), and then it's too late to decorate it.
Is it therefore possible to decorate a whole ASP.NET page (not just an object used by the code behind)? How would I do that?
I am not exactly sure what you wanna decorate, but;
You can create an HttpHandler that lets you do your work on a particular request as follows
public class MyHttpHandler : IHttpHandler
{
public void ProcessRequest(HttpContext context)
{
if (context.Request.RawUrl.Contains("product.aspx"))
{
// may be you can execute your decorate business here
}
}
public bool IsReusable { get { return false; } }
}
Or may be you can use Global.asax 's OnBeginRequest event like as follows
public class Global : HttpApplication
{
void Application_Start(object sender, EventArgs e)
{
// Code that runs on application startup
BundleConfig.RegisterBundles(BundleTable.Bundles);
AuthConfig.RegisterOpenAuth();
RouteConfig.RegisterRoutes(RouteTable.Routes);
base.BeginRequest += OnBeginRequest;
}
private void OnBeginRequest(object sender, EventArgs e)
{
if (Request.RawUrl.Contains("product.aspx"))
{
//execute your business here..
}
}
First of all, according to your comment below the question, I can say that you should implement your showcase as a web user control (ascx)
You can decorate your ShowCase as follows,
public partial class ShowCase : System.Web.UI.UserControl, IShowCase
{
protected void Page_Load(object sender, EventArgs e){}
public void ApplyConfiguration(IConfiguration configuration)
{
throw new NotImplementedException();
}
}
public interface IShowCase
{
void ApplyConfiguration(IConfiguration configuration);
}
public abstract class Decorator : IShowCase
{
protected IShowCase ShowCase;
protected Decorator(IShowCase showcase)
{
ShowCase = showcase;
}
public virtual void ApplyConfiguration(IConfiguration configuration)
{
ShowCase.ApplyConfiguration(configuration);
}
}
public class ShowCaseDecoratorA : Decorator
{
public ShowCaseDecoratorA(IShowCase showcase) : base(showcase){ }
public override void ApplyConfiguration(IConfiguration configuration)
{
base.ApplyConfiguration(configuration);
//depending on the configuration, do something..
ShowCase.Visible = false;
}
}
public interface IConfiguration
{
//configuration
}
Then, from inside the page that uses ShowCase user control, you do something like this,
public partial class _Default : Page
{
protected void Page_Load(object sender, EventArgs e)
{
IConfiguration configuration = ConfigurationFactory.Get();
new ShowCaseDecoratorA(this.ShowCase).ApplyConfiguration(configuration);
}
}
I hope this gives you some inspiration..

Ninject injection chain isolation

I'm working on an application that is split over multiple assemblies. Each of the assemblies provides Interfaces to the outside world, instances are generated via Ninject-based factories.
Ah well, let there be Code. This is from the executing Assembly.
public class IsolationTestModule : NinjectModule
{
public override void Load()
{
ServiceFactory sf = new ServiceFactory();
Bind<IService>().ToMethod(context=>sf.CreatService()).InSingletonScope();
}
}
public partial class App : Application
{
protected override void OnStartup(StartupEventArgs e)
{
IKernel kernel = new StandardKernel(new IsolationTestModule());
IService service = kernel.Get<IService>();
}
}
The ServiceFactory also relies on Ninject, but has its own Kernel and its own Module:
public interface IService
{
void Idle();
}
public interface IDependantService
{
void IdleGracefully();
}
public class ServiceImpl : IService
{
[Inject]
public IDependantService DependantService { get; set; }
public void Idle()
{
DependantService.IdleGracefully();
}
}
public class DependantServiceImpl : IDependantService
{
public void IdleGracefully() { }
}
public class ServiceFactory
{
private IKernel _kernel = new StandardKernel(new SuppliesModule());
public IService CreatService()
{
return _kernel.Get<IService>();
}
}
public class SuppliesModule : NinjectModule
{
public override void Load()
{
Bind<IService>().To<ServiceImpl>().InSingletonScope();
Bind<IDependantService>().To<DependantServiceImpl>().InSingletonScope();
}
}
What actually happens : All's well until the ServiceFactory has completed to build the ServiceImpl-instance. In the next step, the application's kernel tries to resolve ServiceImpl dependencies via IsolationTestModule and - of course - fails with an exception (no binding available, type IDependantService is not self-bindable). In my understanding the factory's kernel should do that...
Actually I never knew Ninject was that eager to resolve dependencies even in those instances it did not immediately create, which surely opens up new horizons to me ;-)
To temporarily solve this, I change the ServiceImpl to Constructor based injection as depicted below:
public class ServiceImpl : IService
{
public IDependantService DependantService { get; set; }
[Inject]
public ServiceImpl(IDependantService dependantService)
{
DependantService = dependantService;
}
public void Idle()
{
DependantService.IdleGracefully();
}
}
Nevertheless, I would prefer a solution that does not force me to change my Injection strategy. Does anyone have an idea how I can separate the Injection chains?
Your observations are correct. Ninject will do property injection for objects created by ToMethod. Also your solution to use constructor injection is the right way to go. Constructor injection is the perfered way to use Ninject anyway. Property Injection should be used for optional dependencies only.
What you should consider is to use just one kernel. It is very unusual to use multiple kernel instances in an application.

Categories