Hello,
this is my second article that I'm writing for bytes.com, and this time I wish to share some knowledge on how to test a custom ActionFilterAtt ribute for a controller in the ASP.NET MVC 2 Framework.
I've been working with the Framework for a month now, comming from a Ruby on Rails background, and developed a special ActionFilter that performs authentication of users against our database using customized HTTP headers.
The concept of the filter, is to check for the presence of these headers and then extract the required information from these headers to perform the authentication. The code for the filter looks somehow like this:
The code explain it's purpose. However, because we use TDD, we needed a UnitTest that can actually emulate a required for this filter and provide the correct attributes, without the filter knowing that it's working with simulated data.
The anwser to this problem : Mock Frameworks.
For the following code, we will rely on the following Frameworks to properly support the code:
- MoQ 4
- Gillio
- Our own Framework (to handle database traffic)
Step:1 Constructing the Mock Objects
The first step in properly testing the functionality of the ActionFilter that was shown above, is to construct the Mock objects that will represent the HttpContext object. In the MVC Framework, this object contains all the information related to a specific HTTP Request or Response. The Tricky part however, is that various properties are read only and cannot be set directly without constructing huge wrappers around it.
So in order to construct the object, the response and the request we use the following calls from the Mock Framework:
Note that these values are stored in properties of the Test class.
On their own, these objects don't do much. We need to tell these Mock objects how to respond to requests from the Filter, otherwise we would be bombarded with null reference exceptions.
When you look at the code for the Filter, it becomes evidence that we need to have access to the following parts:
- The Response and Request property of the HttpContext
- The Headers property of the Response object
- The Headers property of the Request object
- The URL that was used to request the authentication
Because each object is a sub-object of the global ActionContext object, we need to build up our tree of requests step by step. The following code demonstrates how we link the objects together:
Now we have configured our Mock objects to know each other and return something that can be used when a call is made to the Headers, Request or Response property.
Step 2 : performing the unit test
Now we need to actually call the test. Because the filter is a class, we can create an instance from it and run the code with a UnitTest Framework such as NUnit:
What this code does, is call the function that the MVC Framework would call when a request is received that requires authentication.
With the first line of code, we mimic the call of the HTTP Request and insert the custom header that is required by the ActionFilter with the correct value.
The second line of code calls the actuall filter code and performs the entire authentication process that can be seen in the first code snippet.
The hardest part is getting the Mock Framework properly configured in order to mimic the MVC Calls. BEcause I struggled with this myself, and the solution is soo straightforward , I decided to share this insight with everyone.
this is my second article that I'm writing for bytes.com, and this time I wish to share some knowledge on how to test a custom ActionFilterAtt ribute for a controller in the ASP.NET MVC 2 Framework.
I've been working with the Framework for a month now, comming from a Ruby on Rails background, and developed a special ActionFilter that performs authentication of users against our database using customized HTTP headers.
The concept of the filter, is to check for the presence of these headers and then extract the required information from these headers to perform the authentication. The code for the filter looks somehow like this:
Code:
using System;
using System.Text;
using System.Web.Mvc;
using TenForce.Execution.Framework;
using TenForce.Execution.Api2.Implementation;
namespace TenForce.Execution.Web.Filters
{
/// <summary>
/// This class defines a custom Authentication attribute that can be applied on controllers.
/// This results in authentication occuring on all actions that are beeing defined in the controller
/// who implements this filter.
/// </summary>
[AttributeUsage(AttributeTargets.Class | AttributeTargets.Method, Inherited = true, AllowMultiple = true)]
public class AuthenticationFilter : ActionFilterAttribute
{
#region IAuthorizationFilter Members
/// <summary>
/// This function get's called by the Mvc framework prior to performing any actions on
/// the controller. The function will check if a call is authorized by the caller.
/// The function will extract the username and password from the HTTP headers send by
/// the caller and will validate these against the database to see if there is a valid
/// account for the user.
/// If the user can be found in the database, operations will resume, otherwise the action
/// is canceled.
/// </summary>
/// <param name="filterContext">The context for the filter.</param>
public override void OnActionExecuting(ActionExecutingContext filterContext)
{
// Call the base operations first.
base.OnActionExecuting(filterContext);
// Surround the entire authentication process with a try-catch to prevent errors from
// breaking the code.
try
{
// Extract the custom authorization header from the HTTP headers.
string customAuthHeader = Encoding.UTF8.GetString(Convert.FromBase64String(filterContext.RequestContext.HttpContext.Request.Headers["TenForce-Auth"]));
// Split the header in the subcomponents.
string[] components = customAuthHeader.Split('|');
// Check if both components are present.
if (components.Length >= 2)
{
// This header consists of 2 parts, the username and password, seperate by a vertical pipe.
string username = components[0] ?? string.Empty;
string password = components[1] ?? string.Empty;
// Validate the user against the database.
if (Authenticator.Authenticate(username, password))
{
// The request is valid, so add the custom header to inform the request was
// authorized.
AllowRequest(filterContext);
return;
}
}
// If we reach this point, the authorization request is no longer valid.
CancelRequest(filterContext);
}
catch (Exception ex)
{
// Log the exception that has occured.
Logger.Log(GetType(), ex);
// Cancel the request, as we could not properly process it.
CancelRequest(filterContext);
}
}
#endregion
#region Private Methods
/// <summary>
/// Cancels the Athorization and adds the custom tenforce header to the response to
/// inform the caller that his call has been denied.
/// </summary>
/// <param name="authContext">The authorizationContxt that needs to be canceled.</param>
private static void CancelRequest(ActionExecutingContext authContext)
{
authContext.Result = new HttpUnauthorizedResult();
authContext.HttpContext.Response.Headers.Add(@"Custom Response Header", @"Denied value");
}
/// <summary>
/// Allows the Authorization and adds the custom tenforce header to the response to
/// inform the claler that his call has been allowed.
/// </summary>
/// <param name="authContext">The authorizationContext that needs to be allowed.</param>
private static void AllowRequest(ActionExecutingContext authContext)
{
authContext.Result = null;
authContext.HttpContext.Response.Headers.Add(@"Custom Response Header", @"Accepted Value");
}
#endregion
}
}
The anwser to this problem : Mock Frameworks.
For the following code, we will rely on the following Frameworks to properly support the code:
- MoQ 4
- Gillio
- Our own Framework (to handle database traffic)
Step:1 Constructing the Mock Objects
The first step in properly testing the functionality of the ActionFilter that was shown above, is to construct the Mock objects that will represent the HttpContext object. In the MVC Framework, this object contains all the information related to a specific HTTP Request or Response. The Tricky part however, is that various properties are read only and cannot be set directly without constructing huge wrappers around it.
So in order to construct the object, the response and the request we use the following calls from the Mock Framework:
Code:
HttpRequest = new Mock<HttpRequestBase>();
HttpResponse = new Mock<HttpResponseBase>();
HttpContext = new Mock<HttpContextBase>();
ActionContext = new Mock<ActionExecutingContext>();
Filter = new Web.Filters.AuthenticationFilter();
On their own, these objects don't do much. We need to tell these Mock objects how to respond to requests from the Filter, otherwise we would be bombarded with null reference exceptions.
When you look at the code for the Filter, it becomes evidence that we need to have access to the following parts:
- The Response and Request property of the HttpContext
- The Headers property of the Response object
- The Headers property of the Request object
- The URL that was used to request the authentication
Because each object is a sub-object of the global ActionContext object, we need to build up our tree of requests step by step. The following code demonstrates how we link the objects together:
Code:
ActionContext.SetupGet(c => c.HttpContext).Returns(HttpContext.Object);
HttpContext.SetupGet(r => r.Request).Returns(HttpRequest.Object);
HttpContext.SetupGet(r => r.Response).Returns(HttpResponse.Object);
HttpResponse.SetupGet(x => x.Headers).Returns(new System.Net.WebHeaderCollection());
HttpRequest.SetupGet(r => r.RawUrl).Returns(@"http://test.yourdomain.com");
Step 2 : performing the unit test
Now we need to actually call the test. Because the filter is a class, we can create an instance from it and run the code with a UnitTest Framework such as NUnit:
Code:
[Test]
public void SuccessfullAuthentication()
{
// Configure the Request and Response headers before making the call
// to the ActionFilter. Ensure the authentication header is present.
HttpRequest.SetupGet(r => r.Headers).Returns(new System.Net.WebHeaderCollection
{{@"YourHeader", "Header value"}});
// Call the action on the filter and check the response.
Filter.OnActionExecuting(ActionContext.Object);
// Check the ActionResult to null and that the response header contains the correct value.
Assert.IsTrue(ActionContext.Object.Result == null);
Assert.IsTrue(ActionContext.Object.HttpContext.Response.Headers["Response Header"].Equals(@"Response Header Value"));
}
With the first line of code, we mimic the call of the HTTP Request and insert the custom header that is required by the ActionFilter with the correct value.
The second line of code calls the actuall filter code and performs the entire authentication process that can be seen in the first code snippet.
The hardest part is getting the Mock Framework properly configured in order to mimic the MVC Calls. BEcause I struggled with this myself, and the solution is soo straightforward , I decided to share this insight with everyone.