Unit testing ASP.NET MVC redirection - asp.net

How do I Unit Test a MVC redirection?
public ActionResult Create(Product product)
{
_productTask.Save(product);
return RedirectToAction("Success");
}
public ActionResult Success()
{
return View();
}
Is Ayende's approach still the best way to go, with preview 5:
public static void RenderView(this Controller self, string action)
{
typeof(Controller).GetMethod("RenderView").Invoke(self,new object[] { action} );
}
Seems odd to have to do this, especially as the MVC team have said they are writing the framework to be testable.

[TestFixture]
public class RedirectTester
{
[Test]
public void Should_redirect_to_success_action()
{
var controller = new RedirectController();
var result = controller.Index() as RedirectToRouteResult;
Assert.That(result, Is.Not.Null);
Assert.That(result.Values["action"], Is.EqualTo("success"));
}
}
public class RedirectController : Controller
{
public ActionResult Index()
{
return RedirectToAction("success");
}
}

This works for ASP.NET MVC 5 using NUnit:
[Test]
public void ShouldRedirectToSuccessAction()
{
var controller = new RedirectController();
var result = controller.Index() as RedirectToRouteResult;
Assert.That(result.RouteValues["action"], Is.EqualTo("success"));
}
If you want to test that you are redirecting to a different controller (say NewController), the assertion would be:
Assert.That(result.RouteValues["controller"], Is.EqualTo("New"));

You can assert on the ActionResult that is returned, you'll need to cast it to the appropriate type but it does allow you to use state-based testing. A search on the Web should find some useful links, here's just one though.

you can use Mvc.Contrib.TestHelper which provides assertions for testing redirections. Take a look at http://kbochevski.blogspot.com/2010/06/unit-testing-mvcnet.html and the code sample. It might be helpful.

Related

what is response.write in asp.net mvc?

This will be quite simple but
What is the best way of using classical webforms "response.write" in asp net MVC. Especially mvc5.
Let's say: I just would like to write a simple string to screen from controller.
Does response.write exist in mvc?
Thanks.
If the return type of your method is an ActionResult, You can use the Content method to return any type of content.
public ActionResult MyCustomString()
{
return Content("YourStringHere");
}
or simply
public String MyCustomString()
{
return "YourStringHere";
}
Content method allows you return other content type as well, Just pass the content type as second param.
return Content("<root>Item</root>","application/xml");
As #Shyju said you should use Content method, But there's another way by creating a custom action result, Your custom action-result could look like this::
public class MyActionResult : ActionResult
{
private readonly string _content;
public MyActionResult(string content)
{
_content = content;
}
public override void ExecuteResult(ControllerContext context)
{
context.HttpContext.Response.Write(_content);
}
}
Then you can use it, this way:
public ActionResult About()
{
ViewBag.Message = "Your application description page.";
return new MyActionResult("content");
}

Is there a way to get the current controller instance in ASP.NET 5?

Is there a way to do this using DI? I tried IScopedInstance<Controller> but this gives me null. Poked around aspnet's source code but didn't win. Any ideas?
I have a controller that accepts different IPaymentMethods. The IPaymentMethod can be a ViewComponent that can render Views. If the IPaymentMethod is a ViewComponent, I want it to use MVC's built-in model binding on post back.
public class XController : Controller
{
// ctor, props, ...
public IActionResult Checkout()
{
return View(new Model
{
PaymentMethodId = 1,
PaymentMethodType = typeof(MyPaymentMethod) // The razor file will use this type to render it as a ViewComponent
});
}
[HttpPost]
public IActionResult Checkout(Model model)
{
var paymentMethod = _paymentService.GetPaymentMethodById(model.PaymentMethodId);
paymentMethod.ProcessPayment();
// ..
}
}
This is where I need the controller to be injected. I wanted to make use of the built-in MVC validation and model binding.
public class MyPaymentMethod : IPaymentMethod
{
private Controller _currentController;
public MyPaymentMethod(IScopedInstance<Controller> controller)
{
_currentController = controller.Value;
}
public void ProcessPayment()
{
var model = new PaymentModel();
_currentController.TryUpdateModel(model, typeof(PaymentModel), null);
if (!_currentController.ModelState.IsValid)
{
return; // or exception
}
// Process Payment using model
}
public Task<IViewComponentResult> InvokeAsync()
{
// returns View
}
}
public interface IPaymentMethod
{
void ProcessPayment();
}
Since the model instance is required in the ProcessPayment method, why not simply pass it as a parameter?
[HttpPost]
public IActionResult Checkout(PaymentModel model)
{
var paymentMethod = _paymentService.GetPaymentMethodById(model.PaymentMethodId);
if (!ModelState.IsValid)
{
return; // or exception
}
paymentMethod.ProcessPayment(model);
// ..
}
public void ProcessPayment(PaymentModel model)
{
// Process Payment using model
}
Your service is taking on responsibilities that belong to the controller - namely checking ModelState.IsValid.
public interface IPaymentMethod
{
void ProcessPayment(PaymentModel model);
}
You may wish to also pass just the properties that are needed from the payment model, or you may wish to make an IPaymentModel interface to decouple your model from your PaymentService. In that case, your IPaymentModel would go into a shared layer.
public interface IPaymentMethod
{
void ProcessPayment(IPaymentModel model);
}
This no longer works with beta7
At this time of writing (beta6), this probably isn't supported and there is a good reason for it: Controllers in ASP.NET 5 does not need to inherit from the Controller class. I have, however, found a way for this to work using ActionFilters.
public class ScopeControllerActionFilterAttribute : ActionFilterAttribute
{
private readonly IScopedInstance<Controller> _controller;
public ScopeControllerActionFilterAttribute(IScopedInstance<Controller> controller)
{
_controller = controller;
}
public override void OnActionExecuting(ActionExecutingContext context)
{
if (_controller.Value == null)
{
_controller.Value = context.Controller as Controller;
}
}
}
Note that depending on the stage of the http request lifecycle, the Value of IScopedInstance<Controller> may still be empty.

ASP MVC 1.0 - Throwing 404 error from Controller so Web Crawlers will stop indexing

Having Issues trying to throw a 404 error page in MVC 1 from a Controller so that the page actually comes up as page not found. How would I go about doing this?
If you are using MVC1, you may need to create your own HttpNotFoundResult like below and return that from your controller.
public class HttpNotFoundResult : ActionResult {
public HttpNotFoundResult() {
}
public override void ExecuteResult(ControllerContext context) {
context.HttpContext.Response.StatusCode = 404;
}
}
In your action method, you can do.
public ActionResult Index()
{
return new HttpNotFoundResult();
}
From MVC3, you can return HttpNotFound() result directly.
public ActionResult Index()
{
return HttpNotFound();
}
I would add some logic that if your database doesn't return content then return HttpNotFound(); Additionally you can pass a string HttpNotFound("Maybe I deleted it?"); This is very similar to how the WebAPI works.
public ActionResult Index()
{
return HttpNotFound();
}

ASP.NET MVC 5 Common actions for more controllers

I have some controllers (and will be more) which share some actions like those:
public ActionResult DeleteConfirmed(int id)
{
Supplier s = db.Suppliers.Find(id);
s.Deleted = true;
db.SaveChanges();
return RedirectToAction("Index");
}
public ActionResult RestoreConfirmed(int id)
{
Supplier s = db.Suppliers.Find(id);
s.Deleted = false;
db.SaveChanges();
return RedirectToAction("Index");
}
Those action are part of SuppliersController. What this does is that when I delete or restore an object, it marks the object in the database as true for deleted field (and false when it is restored).
The same behavior is shared by many other controllers like CurrenciesController, ProductsController, etc...
In the code I showed you should see that my database entity is clearly specified (Supplier) and also the repository (Suppliers).
I want to find a way to this in a generic way. I want to create a custom controller and all other controllers that shares the same behavior will extended it. In this case ProductsController will extend my DeleteRestoreController.
How can I do this in a "generic" way?
db is a DbContext
public partial class LE: DbContext
{
public LE()
: base("name=LE")
{
}
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
throw new UnintentionalCodeFirstException();
}
public virtual DbSet<Category> Categories { get; set; }
public virtual DbSet<CategoryText> CategoryTexts { get; set; }
...
}
Categories also share the same behavior.
To go one step further
public abstract class DeleteRestoreController<T> : Controller
{
public virtual Action DeleteConfirmed(int id)
{
var dbset = db.Set<T>();
var s = dbset.Find(id);
s.Deleted = true;
db.SaveChanges();
return RedirectToAction("Index");
}
}
then when defining your controller add the entity type
public class ProductsController : DeleteRestoreController<Supplier>
{
////blah
}
You can implement your DeleteRestoreController as an abstract class.
public abstract class DeleteRestoreController : Controller
{
private IRepository : Repository;
public DeleteRestoreController() { ... }
public DeleteRestoreController(IRepository Repository) { ... }
public virtual Action DeleteConfirmed(int id)
{
Supplier s = db.Suppliers.Find(id);
s.Deleted = true;
db.SaveChanges();
return RedirectToAction("Index");
}
}
If you need to differ from that behaviour in your ProductsController you can simply override that method.
public class ProductsController : DeleteRestoreController
{
public override void DeleteConfirmed()
{
//override the logic
}
}
You could always go one step further and implement a generic repository as well, but I've never gone beyond 6-8 controllers in my applications and didn't create one once.
EDIT I've just read in the comments, that the entities would change from Suppliers in the controllers, so implementing a base controller wouldn't make much sense, if you do not implement a generic interface as well. Robert Harvey has made a great point in stating the complexity has to go somewhere.

RedirectToAction alternative

I'm using ASP.NET MVC 3.
I've wriiten a helper class as follows:
public static string NewsList(this UrlHelper helper)
{
return helper.Action("List", "News");
}
And in my controller code I use it like this:
return RedirectToAction(Url.NewsList());
So after the redirect the link looks like this:
../News/News/List
Is there an alternative to RedirectToAction? Is there a better way that I need to implement my helper method NewsList?
Actually you don't really need a helper:
return RedirectToAction("List", "News");
or if you want to avoid hardcoding:
public static object NewsList(this UrlHelper helper)
{
return new { action = "List", controller = "News" };
}
and then:
return RedirectToRoute(Url.NewsList());
or another possibility is to use MVCContrib which allows you to write the following (personally that's what I like and use):
return this.RedirectToAction<NewsController>(x => x.List());
or yet another possibility is to use T4 templates.
So it's up to you to choose and play.
UPDATE:
public static class ControllerExtensions
{
public static RedirectToRouteResult RedirectToNewsList(this Controller controller)
{
return controller.RedirectToAction<NewsController>(x => x.List());
}
}
and then:
public ActionResult Foo()
{
return this.RedirectToNewsList();
}
UPDATE 2:
Example of unit test for the NewsList extension method:
[TestMethod]
public void NewsList_Should_Construct_Route_Values_For_The_List_Action_On_The_News_Controller()
{
// act
var actual = UrlExtensions.NewsList(null);
// assert
var routes = new RouteValueDictionary(actual);
Assert.AreEqual("List", routes["action"]);
Assert.AreEqual("News", routes["controller"]);
}

Resources