Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How can I Unit-test the behaviour of the HandleError attribute for a controller method?

I'm trying to verify the behaviour of my ASP.Net MVC app when an unexpected error occurs. Specifically, I'm trying to verify that the user is directed to the error page I've defined for my app. The problem I'm encountering is that I'm not able to verify the behaviour of the controller method as expected.

For my normal behaviour tests, I create a mock business rule object and pass that to my controller and then verify the ViewResult from the controller method that I want to test. This works fine for my purposes when things work as expected. However, when I throw an exception from the business rule method, the exception is carried up through the controller method result rather than being handled (the controller method has the 'HandleError' attribute) by the controller so that an appropriate ViewResult for my error page being returned.

Is there any way to verify the behaviour of the HandleError attribute in this fashion? Or am I going about this completely wrong? I realize I could use Selenium (in-browser testing which would hit the actual server) to verify the behaviour in an actual browser, but mocking these sort of tests lets me do this faster and with much less overhead...

Sample Test Code :

// WidgetController.Index() makes a call to GetWidgets to retrieve a 
//  List<Widget> instance.

// this works as expected since the appropriate ViewResult is returned 
//  by the controller
public void TestWidgetControllerIndex_NoResultsFound()
{
    var mockBR = new Mock<IBusinessRules> { CallBase = true };
    mockBR.Setup(br=>fr.GetWidgets()).Returns(new List<Widget>());    
    WidgetController controller = new WidgetController(mockBR.Object);
    ViewResult result = (ViewResult)controller.Index();
    Assert.AreEqual("Index", result.ViewName);
    Assert.AreEqual(0, 
        ((WidgetIndexViewData)result.ViewData.Model).Widgets.Count);
}

// this test is unable to reach the assertion statements due to the problem 
//  outlined above. WidgetController.Index has the HandleError attribute 
//  properly applied and the behaviour via the interface is as expected
public void TestWidgetControllerIndex_BusinessRulesExceptionEncountered()
{
    var mockBR = new Mock<IBusinessRules> { CallBase = true };
    mockBR.Setup(br=>fr.GetWidgets()).Throws<ApplicationException>();    
    WidgetController controller = new WidgetController(mockBR.Object);
    ViewResult result = (ViewResult)controller.Index();
    // The ApplicationException thrown by the business rules object bubbles 
    //  up to the test through the line above. I was expecting this to be 
    //  caught and handled by the HandleError filter (which would then let  
    //  me verify the behaviour results via the assertion below)..
    Assert.AreEqual("Error", result.ViewName);
}

I'd appreciate any suggestions as to what I might be doing wrong or whether I'm just approaching this from entirely the wrong direction. I'm making the assumption that testing at the controller method level is that appropriate way to go here since that's where the HandleError attribute is applied.. ( If I do need to test at the application level, is it possible to do that via similar instantiated objects rather than using something like Selenium? )

Update I've come to the conclusion that I shouldn't be testing the functionality related to the HandleError attribute on each controller action. I don't actually care about what it does, I just want to make sure that the error is handled (from my test perspective whether its custom code or the MVC libraries doesn't make a difference, it's the functionality that I want to verify).

What I've ended up doing is wrapping my controller actions in try/catch blocks in order to force the Error view to be returned as a result of the method (rather than the ErrorHandler attribute catching the error as it leaves the method). This way I can assert in my unit tests that the error is properly handled with appropriate feedback. I'm not very pleased with the extra length that this adds to my controller methods, but it does let me provide a friendly, specific error message to the user (I'm using an extension method to display the feedback and perform logging). (So there are pros and cons to the try/catch approach for sure..)

I'm not 100% positive that this is the cleanest way to go, but it achieves my goal of being able to verify that errors are handled via controller unit-tests (fast) rather than having to execute tests in a browser (slow). So basically it's good enough for now, until I can find a cleaner solution. I've decided to offer a bounty if anyone encounters a similar problem and has found a better solution..

like image 517
Peter Bernier Avatar asked Apr 22 '09 19:04

Peter Bernier


1 Answers

I think you cannot unit test this. Nor do you want to. You can test that the expected exception is thrown by a controller method. Using reflection you can test that the action or controller has the attribute that you expect it to have and that the attribute has certain expected property values. However, the job of intercepting the exception and executing the attribute is behavior of the framework, not your code. Generally speaking you should not test code that is not yours (the framework).

like image 175
Tim Scott Avatar answered Oct 31 '22 16:10

Tim Scott