I have a class that inherits from System.Web.Mvc.RedirectResult which overrides ExecuteResult. How do I unit test this?
Thanks.
Depends on what you override does.
In general, mock HttpContextBase, create the necessary RouteData and use those two elements to create a ControllerContext.
Instantiate your custom RedirectResult, call ExecuteResult passing in your previously created ControllerContext and then make assertions on the various bits and pieces that you mocked.
If you want a more comprehensive answer, I would need more details as to exactly what your override does.
Related
I heard the following sentence while explaining the request Life Cycle of MVC.
When the UrlRoutingModule intercepts a request, the first thing the
module does is to wrap up the current HttpContext in an
HttpContextWrapper2 object.
What is the significance of wrapping the HttpContext ?
The module passes the wrapped HttpContext to the RouteTable. The
HttpContext includes the URL, form parameters, query string
parameters, and cookies associated with the current request. If a
match can be made between the current request and one of the Route
objects in the Route Table, then a RouteData object is returned.
If the UrlRoutingModule successfully retrieves a RouteData object then
the module next creates a RouteContext object
what is the meaning of RouteContext?
I searched a lot on google but could not found any reference.
I believe the main reason is to facilitate unit testing.
HttpContextWrapper2 inherits from HttpContextBase which is an abstract class with the same members as HttpContext. MVC Types expose properties as HttpContextBase rather than HttpContext (e.g. Controller.HttpContext)) and this means that unit testing can use a concrete derived class that runs outside the ASP.NET pipeline, facilitating unit testing.
Can you give a code example or any link ?
From this MSDN article on unit testing MVC:
In traditional ASP.NET, one of the obstacles that developers come across during testing is the plethora of static classes used during each request. The ASP.NET MVC team made the decision to wrap many of the .NET static helper classes (such as HttpContext and HttpRequest) so that they can be replaced during testing with a stub. ASP.NET MVC provides many abstractions to help developers avoid using these classes, but in places where you are required to use them, the wrappers make this code easier to test.
The ASP.NET MVC documentation is a bit thin on the ground, for a deep understanding I'd recommend the book "Pro ASP.NET MVC4 Framework".
If you would cite the source you've got the sentence from:
When the UrlRoutingModule intercepts a request, the first thing the module does is to wrap up the current HttpContext in an HttpContextWrapper2 object. The HttpContextWrapper2 class, unlike the normal HttpContext class, derives from the HttpContextBase class. Creating a wrapper for HttpContext makes it easier to mock the class when you are using a Mock Object Framework such as Typemock Isolator or Rhino Mocks.
var mockedService = new Mock<IService>();
mockedService.Setup(x => x.InterfaceMethod(args)).Returns(value);
_Service = mockedService.Object;
MyController controller =new MyController(_Service);
var result = (ViewResult)controller.Foo();
Now this Foo() Method contains the Following API Call
HttpContext.GetGlobalResourceObject(...,..);
But the HTTPContext is null, as i'm just unit testing , i have not Authenticated. and hence not populated the HTTPContext Object.
I cant set expectations on this class because the HTTPContext is sealed.
I've been searching in the net , but there were only samples to fake the HTTPContextBase
Now how do i get my Unit Test to Pass. Any Suggestions.?
thanks ,
vijay
The problem is you are not setting up an expectation for the "Centres" method. When you mock an object all implementations are replaced with a Noop until you specify otherwise using an Expectation.
I'm guessing you mocked GetValueFromResource rather than Centres because Centres calls GetValueFromResource. If this is the case, mocking GetValueFromResource is likely a superfluous fake and you don't need it.
Also, as has been pointed out on several other Moq posts, you'll need to make sure that you are not mocking what you are testing. If you are actually trying to test MyController, you'd want to mock any dependencies it has, but not MyController itself. This will cause you no end of confusion.
Edit
Based on this feedback it seems you have learned that your true problem is that you need to mock HTTPContext. Mocking HTTPContext is nontrivial. There are a few tools that can do it natively, like Type Mock, but the problem is that ambient context is not testable.
What you should be doing is requesting dependencies that give you the information you need while abstracting away their implementation. For example, in your case it seems like you are using HTTPContext to access resources. If you need something like this a good interface you could use would be:
public interface IResourceStore
{
object GetGlobalResource(string classKey, string resourceKey);
}
Your default implementation would use HTTPContext, but now you have an interface you can mock and have your MyController class use, rather than HTTPContext directly.
Mock<IResourceStore> resourceStore = new Mock<IResourceStore>();
//setup resourceStore expectations here
MyController testingTarget = new MyController(_Service, resourceStore.Object);
//target assertions here
Hope this helps.
Both have Request and Response properties, but I can't write a method that takes either HttpContext or HttpContextBase. In some places either one or the other is available so I need to handle both. I know HttpContextWrapper can convert in one direction, but still... why is it like this?
HttpContext has been around since .NET 1.0. Because of backward compatibility reasons, they can't change that class. HttpContextBase was introduced in ASP.NET MVC to allow for better testability because it makes it easier to mock/stub it.
This is an old question but I just had the same problem and the answer is in Gunder's comment.
Create you methods to use HttpContectBase and then wrap your context in a HttpContextWrapper when you want to call it from your code
public class SomeClass{
... other stuff in your class
public void MyMethod(HttpContextBase contextbase){
...all your other code
}
}
Usage
var objSomeClass = new SomeClass();
objSomeClass.MyMethod(new HttpContextWrapper(HttpContext.Current));
I think HttpContext.Current will be null if you make this call via ajax, I will investigate how to get the context and update this post.
I asked a question earlier today about ActionFilters in ASP.Net MVC. It turned out my problem was really that my ActionFilter is not even running. Among other things I read this article, and I can't find anything he does that I don't.
This is my code:
// The ActionFilter itself
public class TestingIfItWorksAttribute : ActionFilterAttribute
{
public override void OnActionExecuting(ActionExecutingContext filterContext)
{
filterContext.Controller.TempData["filter"] = "it worked!";
base.OnActionExecuting(filterContext);
}
}
// The Controller Action with the filter applied
[TestingIfItWorks]
public ActionResult Test()
{
var didit = TempData["filter"];
return View();
}
A breakpoint in the filter method is never hit when I debug, and TempData["filter"] holds a null value when the view is rendered.
Why is this not working?
In case it's helpful to anyone using MVC 4/5:
ActionFilters don't run if you get the namespace of your ActionFilterAttribute or IActionFilter wrong: https://stackoverflow.com/a/13710468/188926
Use System.Web.Http.Filters for Web API, System.Web.Mvc for standard MVC actions.
As in the question, the filter attribute will simply be ignored (no error) if you get it wrong, which makes it difficult to diagnose.
Based on your comments to another answer
When testing via unit tests, the filter is not invoked. If you want to invoke the filter then you'll need mimic the ControllerActionInvoker. It's probably better, to test the filter itself in isolation, then use reflection to ensure that the filter is applied to your action with the correct attributes. I prefer this mechanism over testing the filter and action in combination.
Original
Surely you need an override on your method otherwise you aren't actually replacing the method on the base class. I would have expected the compiler to complain that you needed either a new or override on it. If you don't include the override keyword, it will behave as if you used new. Since the framework invokes it as an ActionFilterAttribute, this means that your method will never get called.
Quoting from MSDN:
If the method in the derived class is
not preceded by new or override
keywords, the compiler will issue a
warning and the method will behave as
if the new keyword were present.
In addition to what tvanofosson said, your action method isn't actually rendering anything to the view. Does your view have a <%=TempData["Filter"].ToString()%> statement or something similar?
Is there any way I can mock FormsAuthentication.Authenticate("username", "password") method with test credential? My test goal is to make sure that if authentication fails, it redirects to correct place. I'm using Rhino Mocks as mocking framework.
Thank you very much for your help,
Since you are using mvc, if you create a new project and look in the Account controller's code you will find the following clarification:
The FormsAuthentication type is sealed
and contains static members, so it is
difficult to unit test code that calls
its members. The interface and helper
class below demonstrate how to create
an abstract wrapper around such a type
in order to make the AccountController
code unit testable.
Below this, there are these declarations:
public interface IFormsAuthentication
public class FormsAuthenticationService : IFormsAuthentication
public interface IMembershipService
public class AccountMembershipService : IMembershipService
You may look for implementations in the referred code.
I don't use Rhino Mocks, but I think it is not possible to mock static methods with any .Net mocking framework except typemock. The best work around is to create an interfaced adapter around it.