How can I unit test HandleUnknownAction() of an ASP.NET MVC Controller? - asp.net-mvc

I have the following HandleUnknownAction set on my base controller class:
protected override void HandleUnknownAction(string action)
{
Response.Redirect("/");
}
How can I unit test that? Another point, is that way to handle the unknown action correct? Seems that calling RedirectToAction() would be more correct but the HandleUnknownAction doesn't have a return value.
The far I could get to test that is:
[Test]
public void TestHandleUnknownAction()
{
ctroler.ActionInvoker.InvokeAction(ctroler.ControllerContext, "unknown");
}
I'm stuck at it.

I don't think there's a need to test that HandleUnknownAction is invoked when a controller is missing an action. We trust the framework to handle that. So we can test the implementation by calling HandleUnknownAction directly with the mocking framework Moq. Should also be possible with Rhino Mocks.
public void TestHandleUnknownAction()
{
Mock<ControllerContext> cc = new Mock<ControllerContext>
(MockBehavior.Strict);
cc.Expect(c => c.HttpContext.Response.Redirect("/"));
TestHelperController controller = new TestHelperController();
controller.ControllerContext = cc.Object;
controller.InvokeUnknownAction("test");
}
Where TestHelperController makes the HandleUnknownAction accessible:
public class TestHelperController : RealController
{
public void InvokeUnknownAction(string action)
{
this.HandleUnknownAction(action);
}
}

That's fine for a simple Response.Redirect but that unit test code will not work if you want to do something more complicated like rendering an error view:
// TODO - Put some stuff into ViewData or a model
View("Error").ExecuteResult(Me.ControllerContext)

Related

How to perform unit testing on a void method in web api controller

Hi I have a update method in webAPI and that is a void method and I want to perform unit testing on that method.How do I do that??
Not Found any solution.
Below is webapi controller method :-
[HttpPut]
public void UpdatePushNotification(PushNotificationQueueBO pushnotificationqueueBO)
{
PushNotificationQueueBO.UpdatePushNotificationQueue(pushnotificationqueueBO);
}
Below is the unit test case for above method
[TestMethod]
public void UpdatePushNotificationQueue_ShouldUpdate()
{
var item = GetDemoPushNotificationQueue();
var controller = new PushNotificationQueueController();
var result = controller.UpdatePushNotification(item) as ;
Assert.IsNotNull(result);
}
I want what do I write after as in
var result = controller.UpdatePushNotification(item) as ???
The controller method is void so there is no return type and nothing to cast it to.
I believe this to be an XY problem.
void controller actions will always return HTTP Status Code 200 OK at run-time except when an exception is thrown.
Based on the tags in original post the assumption is that the mentioned controller is an ApiController
which means that the controller can be refactored to
[HttpPut]
public IHttpActionResult UpdatePushNotification(PushNotificationQueueBO pushnotificationqueueBO) {
PushNotificationQueueBO.UpdatePushNotificationQueue(pushnotificationqueueBO);
return Ok();
}
There is also the option to wrap it in a try-catch in case of errors
[HttpPut]
public IHttpActionResult UpdatePushNotification(PushNotificationQueueBO pushnotificationqueueBO) {
try {
PushNotificationQueueBO.UpdatePushNotificationQueue(pushnotificationqueueBO);
return Ok();
} catch (Exception ex) {
return InternalServerError(ex);
//OR
//return InternalServerError()
}
}
But that is more of a cross-cutting concern that can be handled by action filters.
This would then allow for an actual return type to be asserted
//...omitted for brevity
IHttpActionResult result = controller.UpdatePushNotification(item);
Assert.IsNotNull(result);
The PushNotificationQueueBO business object however, appears to be making a static member call.
PushNotificationQueueBO.UpdatePushNotificationQueue(pushnotificationqueueBO);
This makes it difficult to unit test the encapsulated API method call in isolation and may result in undesired behavior.
It is suggested that the static business object call be encapsulated behind an abstraction and implementation that can be replaced by a mock when testing in isolation.
You can test a void function in different ways and it depends on what the void method does. For example, if a void method increments the numeric value of a property of its class, then you can use that property in your test. In your case, your void method performs this action;
PushNotificationQueueBO.UpdatePushNotificationQueue(pushnotificationqueueBO);
Firstly, identify what this action does and what it affects. For example, if this method's action performs a manipulation on a queue object, then you can test this object as a result of the void method.

How to intercept all the ASP.NET WebApi controller action methods calls with Ninject interception for logging?

Our company has the need to log certain things each time one of our action methods of our ASP.NET WebApi controllers gets called. Since we use Ninject for the DI right now, we'd like to use it also for this purpose. This is what I have tried so far.
I have Ninject, Ninject.Extensions.Interception and Ninject.Extensions.Interception.DynamicProxy installed through NuGet and I have the following module
public class InterceptAllModule : InterceptionModule
{
public override void Load()
{
Kernel.Intercept(p => p.Request.Service.Name.EndsWith("Controller")).With(new TimingInterceptor());
}
}
Where TimingInterceptor is
public class TimingInterceptor : SimpleInterceptor
{
readonly Stopwatch _stopwatch = new Stopwatch();
protected override void BeforeInvoke(IInvocation invocation)
{
_stopwatch.Start();
}
protected override void AfterInvoke(IInvocation invocation)
{
_stopwatch.Stop();
string message = string.Format("[Execution of {0} took {1}.]",invocation.Request.Method,_stopwatch.Elapsed);
Log.Info(message + "\n");
_stopwatch.Reset();
}
}
Now, when I try to hook the module up with ninject kernel, and run my site
var kernel = new StandardKernel(new InterceptAllModule());
However, whenever there is a call coming in to one of the action method, it throws an error saying
Cannot instantiate proxy of class: MyApiController.
Could someone with experience point out what I'm doing wrong please? Thanks.
Update
So using your Code and Remo's excellent point about needing the action methods to be virtual and putting in an empty default constructor (just to placate dynamic proxy, keep your other constructor still) I have got both the action filter and the interception approach working.
I would say that as it stands your code will intercept potentially unwanted methods on the ApiController so you will probably also need to put some code in place to filter these out e.g. ExecuteAsync and Dispose.
My only other point is performance. Huge disclaimer these are just very basic tests (using the action filter approach each time to log the stats), I invite you to do your own(!)... but using the DynamicProxy interceptor I was getting a time of around 4 milliseconds per get request
[Execution of Get took 00:00:00.0046615.]
[Execution of Get took 00:00:00.0041988.]
[Execution of Get took 00:00:00.0039383.]
Commenting out the Interception code and using an Action filter I was getting sub millisecond performance:
[Execution of Get took 00:00:00.0001146.]
[Execution of Get took 00:00:00.0001116.]
[Execution of Get took 00:00:00.0001364.]
It is up to you whether this is actually an issue or concern but I thought I would point this out.
Previous Response
Have you rulled out using ActionFilters? This is the natural extension point for AOP on an MVC action.
If you were interested in methods other than the actual action on the controller then I would understand but I thought I would post a suggestion anyway.
Inspired by Are ActionFilterAttributes reused across threads? How does that work? and Measure Time Invoking ASP.NET MVC Controller Actions.
Updated to show the exclusion of the timer when method tagged. Inspiration from core WebApi framework specifically AllowAnonymousAttribute and AuthorizeAttribute
Register this globally so that all actions are monitored by this:
GlobalConfiguration.Configuration.Filters.Add(new TimingActionFilter());
Then:
public class TimingActionFilter : ActionFilterAttribute
{
private const string Key = "__action_duration__";
public override void OnActionExecuting(HttpActionContext actionContext)
{
if (SkipLogging(actionContext))
{
return;
}
var stopWatch = new Stopwatch();
actionContext.Request.Properties[Key] = stopWatch;
stopWatch.Start();
}
public override void OnActionExecuted(HttpActionExecutedContext actionExecutedContext)
{
if (!actionExecutedContext.Request.Properties.ContainsKey(Key))
{
return;
}
var stopWatch = actionExecutedContext.Request.Properties[Key] as Stopwatch;
if(stopWatch != null)
{
stopWatch.Stop();
var actionName = actionExecutedContext.ActionContext.ActionDescriptor.ActionName;
Debug.Print(string.Format("[Execution of {0} took {1}.]", actionName, stopWatch.Elapsed));
}
}
private static bool SkipLogging(HttpActionContext actionContext)
{
return actionContext.ActionDescriptor.GetCustomAttributes<NoLogAttribute>().Any() ||
actionContext.ControllerContext.ControllerDescriptor.GetCustomAttributes<NoLogAttribute>().Any();
}
}
And
[AttributeUsage(AttributeTargets.Method | AttributeTargets.Class, Inherited = true)]
public class NoLogAttribute : Attribute
{
}
Now you can exclude the global filter using:
public class ExampleController : ApiController
{
// GET api/example
[NoLog]
public Example Get()
{
//
}
}
For anyone still lurking, the reason I wanted to use Ninject was so I could inject a logger (or anything else) into the interceptor, but I wanted to intercept all actions.
Mark's answer is perfect, but instead of registering globally using
GlobalConfiguration.Configuration.Filters.Add(new TimingActionFilter());
bind your filter with Ninject using
Kernal.BindHttpFilter<TimingActionFilter>(FilterScope.Action).
You'll need to create an appropriate contructor in the TimingActionFilter class.

Unit test an Action with Recaptcha

How I can test the following Action of the controller:
public ActionResult Edit(User usr)
{
if (!Microsoft.Web.Helpers.ReCaptcha.Validate(ConfigurationManager.AppSettings["reCaptchaPrivate"].ToString()))
{
ModelState.AddModelError("reCaptcha", PPRR.App_LocalResources.Global.ErrorFillReCaptcha);
return PartialView("Wrong", usr);
}
if (ModelState.IsValid)
{code..... }}
I would start by abstracting the Captcha validation code:
public interface ICaptchaValidator
{
bool Validate();
}
and then have my controller look like this:
public class FooController: Controller
{
private readonly ICaptchaValidator _validator;
public FooController(ICaptchaValidator validator)
{
_validator = validator;
}
public ActionResult Edit(User usr)
{
if (!_validator.Validate())
{
ModelState.AddModelError("reCaptcha", PPRR.App_LocalResources.Global.ErrorFillReCaptcha);
return PartialView("Wrong", usr);
}
...
}
}
Now you have weaken the coupling between your controller and the way those captchas are validated. That's a good thing as it makes your controller action far easier to unit test. We have successfully made our controller independent of the actual way validation is implemented.
Now just pick a mocking framework such as Rhino Mocks, Moq, NSubstitute and in your unit test inject a stubbed validator into this controller so that you can define behaviors on it.
Personally I would recommend you MvcContrib.TestHelper (which is based on Rhino Mocks) to test your ASP.NET MVC applications. It has many built-in goodies for mocking the HttpContext and make unit testing easy.
Here's an example of how the validation failure case could be tested:
[TestMethod]
public void FooController_Edit_Action_Should_Return_The_Wrong_Partial_If_Captcha_Validation_Fails()
{
// arrange
var validatorStub = MockRepository.GenerateStub<ICaptchaValidator>();
var sut = new HomeController(validatorStub);
var user = new User();
validatorStub.Stub(x => x.Validate()).Return(false);
// act
var actual = sut.Edit(user);
// assert
actual
.AssertPartialViewRendered()
.ForView("Wrong")
.WithViewData<User>()
.Equals(user);
Assert.IsFalse(sut.ModelState.IsValid);
}
As an alternative to Darin's answer, I've previously used a custom ActionFilter that processes the captcha and adds the error to the ModelState. It worked really well and meant that the captcha code wasn't part of the action method itself.

Response Object is a null reference in my Controller's action method

I'm developing a webapp using ASP.NET MVC and C#. And I'm creating a unit test for this webapp using NUnit and Rhino Mock. My problem is that I have a Response object in my controller's action method and when I execute my unit test my test is failing because the Response object is a null reference.
Do I need to separate this Response object call in my actions or there is a better way to resolve this?
public ActionResult Login( string user, string password )
{
Response.Cookies[ "cookie" ].Value = "ck";
...
return View();
}
Please advise.
Many thanks.
What the controller really lacks is its HttpContext. In a test method it should be added explicitly if needed:
[Test]
public void TestMethod()
{
// Assume the controller is created once for all tests in a setup method
_controller.ControllerContext.HttpContext = new DefaultHttpContext();
var result = _controller.Login("username", "verySaf3Passw0rd");
// Asserts here
}
This is one of the annoying points where ASP.NET MVC is not as testable and loosely coupled as it could be. See this question for some suggestions how to mock the HTTP context objects.
I ended up creating a real response that my mock context returns like this...
Mock<HttpSessionStateBase> mockSession;
Mock<ControllerContext> mockContext;
Mock<ISessionProvider> mockSessionProvider;
HttpResponse testResponse;
MyController controller;
[TestInitialize]
public void Initialize()
{
testResponse = new HttpResponse(TextWriter.Null);
mockContext = new Mock<ControllerContext>();
mockSession = new Mock<HttpSessionStateBase>();
mockContext.Setup(x => x.HttpContext.Session).Returns(mockSession.Object);
mockContext.Setup(x => x.HttpContext.Response).Returns(new HttpResponseWrapper(testResponse));
controller = new MyController();
controller.ControllerContext = mockContext.Object;
}

Mock a web service used in an action filter

I have an external-to-my-solution web service that I'm using in an ActionFilter. The action filter grabs some basic data for my MasterPage. I've gone back and forth between using an action filter and extending the base controller class, and decided the action filter was the best approach. Then I started unit testing (Yeah, yeah TDD. Anyway... :D )
So I can't mock (using Moq, btw) a web service in an action filter because I can't inject my mock WS into the action filter, since action filters don't take objects as params. Right? At least that's what I seem to have come to.
Any ideas? Better approaches? I'm just trying to return a warning to the user that if the web service is unavailable, their experience might be limited.
Thanks for any help!
namespace MyProject.ActionFilters
{
public class GetMasterPageData : ActionFilterAttribute
{
public ThatWS ws = new ThatWS();
public override void OnActionExecuting(ActionExecutingContext filterContext)
{
HttpContextBase context = filterContext.HttpContext;
try {
DoStuff();
}
catch ( NullReferenceException e ) {
context.Session["message"] = "There is a problem with the web service. Some functionality will be limited.";
}
}
}
}
Here's a quick and dirty approach:
public class GetMasterPageData : ActionFilterAttribute
{
public Func<ISomeInterface> ServiceProvider = () => new ThatWS();
public override void OnActionExecuting(ActionExecutingContext filterContext)
{
var result = ServiceProvider().SomeMethod();
...
}
}
And in your unit test you could instantiate the action filter and replace the ServiceProvider public field with some mocked object:
objectToTest.ServiceProvider = () => new SomeMockedObject();
Of course this approach is not as clean as the one suggested by #Ryan in the comments section but it could work in some situations.

Resources